From 493203fe7ac0ac40d911fbc2056f43a8f477a5bd Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 16 Mar 2021 04:10:44 +0000 Subject: [PATCH] import systemd-239-45.el8 --- .gitignore | 1 + .systemd.metadata | 1 + ...-whether-struct-statx-is-defined-in-.patch | 105 + ...nd-set-RemoveIPC-to-false-by-default.patch | 50 + ...tTasksMax-to-80-of-the-kernel-pid.ma.patch | 53 + ...mounted-as-tmpfs-without-the-user-s-.patch | 51 + ...m-number-of-process-in-user-slice-to.patch | 35 + ...utomatically-online-hot-plugged-CPUs.patch | 33 + ...or-naming-Dell-iDRAC-USB-Virtual-NIC.patch | 37 + .../0008-rules-enable-memory-hotplug.patch | 22 + ...ctl-settings-when-the-bridge-module-.patch | 22 + SOURCES/0010-rules-load-sg-module.patch | 21 + ...om-character-device-node-permissions.patch | 21 + ...iver-also-when-scsi_target-appears-4.patch | 22 + ...3-rules-don-t-hoplug-memory-on-s390x.patch | 23 + ...to-online-of-hot-plugged-memory-on-I.patch | 24 + ...old-style-by-path-symlinks-for-FCP-b.patch | 39 + ...0016-Revert-udev-remove-WAIT_FOR-key.patch | 123 + ...llow-renaming-interfaces-that-were-r.patch | 22 + ...icUser-yes-from-systemd-resolved.ser.patch | 23 + ...-journal-remove-journal-audit-socket.patch | 73 + ...BUS_DONT_DESTROY-calls-after-asserts.patch | 117 + ...raise-POOL_SIZE_MIN-constant-to-1024.patch | 23 + ...-support-for-sector-size-option-9936.patch | 119 + ...t-define-arg_sector_size-if-libgcryp.patch | 29 + ...le-per-service-IP-firewall-by-defaul.patch | 112 + ...ot-crash-on-message-with-a-string-of.patch | 45 + ...nd_strndup-and-use-it-in-bus-message.patch | 279 ++ ...27-tests-backport-test_setup_logging.patch | 34 + ...upport-URL-shown-in-the-catalog-entr.patch | 23 + ...e-etc-resolv.conf-symlink-at-runtime.patch | 48 + ...-image-use-right-comparison-function.patch | 27 + ...leak-of-name-returned-by-uid_to_name.patch | 60 + ...d-an-assert-that-we-re-not-overwriti.patch | 36 + ...id-calling-ftruncate-with-invalid-fd.patch | 29 + ...we-have-enough-space-for-the-DHCP6-o.patch | 33 + ...ueued_message-pending_reload_message.patch | 133 + ...-t-send-the-pending-reload-message-s.patch | 36 + ...e-don-t-throttle-change-signal-gener.patch | 114 + ...ine-introduce-PROC_CMDLINE_RD_STRICT.patch | 45 + ...introduce-rd.-version-of-all-options.patch | 77 + ...let-s-rework-the-recursive-logic-to-.patch | 213 + ...also-drop-ACLs-when-recursively-chow.patch | 58 + ...own-recursive-TAKE_FD-is-your-friend.patch | 34 + ...dd-test-case-for-recursive-chown-ing.patch | 200 + ...request-ECN-on-both-in-and-outgoing-.patch | 32 + ...-not-try-to-read-all-of-proc-cpuinfo.patch | 84 + ...ee-code-paths-which-free-struct-bus_.patch | 166 + ...d-bus-properly-initialize-containers.patch | 27 + ...rator-introduce-basic-keydev-support.patch | 240 ++ ...-t-use-m-if-there-s-no-error-to-show.patch | 33 + ...ator-don-t-return-error-if-target-di.patch | 38 + ...ator-allow-whitespace-characters-in-.patch | 129 + ...tch-metadata-changes-on-DASD-devices.patch | 25 + ...net.ipv4.conf.all.rp_filter-from-1-t.patch | 41 + ...-enable-user-namespaces-for-TEST-13-.patch | 36 + ...etns-checking-a-bit-for-compat-with-.patch | 122 + ...rop-SKIP_INITRD-for-QEMU-based-tests.patch | 93 + ...-meson-rename-Ddebug-to-Ddebug-extra.patch | 42 + ...hether-gnutls-supports-TCP-fast-open.patch | 38 + ...nit-don-t-add-Requires-for-tmp.mount.patch | 24 + ...recondition-check-for-inherited-flag.patch | 42 + ...alizing-state-always-use-read_line-L.patch | 234 ++ ...imit-on-STATUS-texts-recvd-from-serv.patch | 44 + ...-travis-enable-Travis-CI-on-CentOS-7.patch | 257 ++ SOURCES/0064-travis-RHEL8-support.patch | 175 + ...s-drop-the-SELinux-Fedora-workaround.patch | 37 + ...avis-fix-syntax-error-in-.travis.yml.patch | 24 + ...t-the-container-before-running-tests.patch | 40 + ...duplicate-MESSAGE-prefix-from-messag.patch | 35 + .../0069-journald-remove-unnecessary.patch | 34 + ...store-the-iovec-entry-for-process-co.patch | 202 + ...il-limit-command-line-lengths-to-_SC.patch | 159 + ...sage-when-we-fail-to-save-a-journald.patch | 33 + ...se-functionality-to-query-total-memo.patch | 104 + ...0074-basic-prioq-add-prioq_peek_item.patch | 114 + ...e-number-of-entries-in-the-cache-bas.patch | 81 + ...dically-drop-cache-for-all-dead-PIDs.patch | 77 + ...-t-use-overly-large-buffer-to-store-.patch | 71 + ...switch-net.ipv4.conf.all.rp_filter-f.patch | 26 + ...-journal-fix-syslog_parse_identifier.patch | 68 + ...t-a-limit-on-the-number-of-fields-1k.patch | 54 + ...ocessing-a-native-message-bail-more-.patch | 205 + ...he-maximum-entry-size-limit-to-for-n.patch | 37 + ...nup-function-to-call-MHD_destroy_res.patch | 198 + ...mote-verify-entry-length-from-header.patch | 109 + ...et-a-limit-on-the-number-of-fields-i.patch | 56 + ...ly-attribute-log-messages-also-with-.patch | 54 + .../0087-test-replace-echo-with-socat.patch | 50 + ...ore-tunnel-devices-automatically-add.patch | 25 + ...evator-kernel-command-line-parameter.patch | 42 + ...check-allow-PROGRAM-as-an-assignment.patch | 33 + ...-implement-new-memory-hotplug-policy.patch | 50 + ...GTM-make-LGTM.com-use-meson-from-pip.patch | 27 + SOURCES/0093-lgtm-use-python3.patch | 21 + ...-use-print-function-in-Python-3-code.patch | 73 + ...m-query-for-catching-the-use-of-fget.patch | 44 + .../0096-lgtm-drop-redundant-newlines.patch | 28 + ...le-that-adds-elevator-kernel-command.patch | 24 + ...-UNIT-TESTS-running-all-basic-tests-.patch | 176 + ...-asan-wrapper-automatically-if-syste.patch | 53 + ...per-for-when-systemd-is-built-with-A.patch | 63 + ...t-ASAN-reports-on-journald-to-a-file.patch | 29 + ...an-wrapper-to-boot-a-VM-container-if.patch | 51 + ...ing-additional-arguments-to-nspawn-v.patch | 24 + ...EST-01-BASIC-in-an-unprivileged-cont.patch | 86 + ...n-t-overwrite-TESTDIR-if-already-set.patch | 30 + ...ine_begins-to-accept-word-matching-f.patch | 48 + ...age-paths-longer-than-BUS_PATH_SIZE_.patch | 49 + ...ry-strings-to-hold-dbus-paths-on-the.patch | 189 + ...eive-an-invalid-dbus-message-ignore-.patch | 55 + ...drop-misplaced-Wl-undefined-argument.patch | 49 + ...step-back-again-for-nspawn-we-actual.patch | 40 + ...ree-wide-shorten-error-logging-a-bit.patch | 795 ++++ ...-simplify-machine-terminate-bus-call.patch | 98 + ...merge-two-variable-declaration-lines.patch | 27 + ...n-rework-how-we-allocate-kill-scopes.patch | 125 + ...oup-empty-check-event-if-the-last-re.patch | 31 + ...-journal-remove-journal-audit-socket.patch | 75 + ...able-systemd-journald-audit.socket-b.patch | 39 + ...ey-color-for-de-emphasizing-journal-.patch | 49 + ...its-add-Install-section-to-tmp.mount.patch | 24 + ...y-errno-when-NSS_STATUS_NOTFOUND-or-.patch | 957 +++++ ...util.h-add-new-UNPROTECT_ERRNO-macro.patch | 103 + ...t-errno-before-writing-to-NSS-errnop.patch | 367 ++ ...ogging-about-failure-to-add-syscall-.patch | 309 ++ ...n-duplicating-a-cell-also-copy-the-c.patch | 25 + ...ionally-make-specific-cells-clickabl.patch | 203 + ...ore-outputting-a-color-check-if-colo.patch | 40 + ...-option-to-store-format-percent-and-.patch | 147 + ...ionally-allow-reversing-the-sort-ord.patch | 77 + ...-table_update-to-update-existing-ent.patch | 77 + ...-an-API-for-getting-the-cell-at-a-sp.patch | 47 + ...t-table-always-underline-header-line.patch | 41 + ...-calls-to-query-the-data-in-a-specif.patch | 54 + ...e-sure-we-never-call-memcmp-with-NUL.patch | 47 + ...at-table-use-right-field-for-display.patch | 29 + ...-option-to-uppercase-cells-on-displa.patch | 165 + ...er-try-to-reuse-cells-that-have-colo.patch | 36 + ...logic-to-output-smiley-emojis-at-var.patch | 207 + .../0139-analyze-add-new-security-verb.patch | 2310 +++++++++++ ...mentary-fuzzer-for-server_process_sy.patch | 66 + ...-clear-that-dev_kmsg_record-modifies.patch | 30 + ...e-allocated-memory-before-returning-.patch | 28 + ...sts-rework-the-code-fuzzing-journald.patch | 89 + ...rver_process_native_message-compatib.patch | 40 + ...er-for-server_process_native_message.patch | 46 + ...0146-tests-add-a-fuzzer-for-sd-ndisc.patch | 98 + .../0147-ndisc-fix-two-infinite-loops.patch | 34 + ...ucers-for-several-issues-uncovered-w.patch | 70 + ...oducer-for-an-infinite-loop-in-ndisc.patch | 46 + ...oducer-for-another-infinite-loop-in-.patch | 37 + ...e-fuzz-corpus-directory-to-just-fuzz.patch | 164 + ...testcase-for-issue-10007-by-oss-fuzz.patch | 37 + ...uzz-regressions-directory-with-the-m.patch | 240 ++ ...shal-use-cescaping-instead-of-hexmem.patch | 50 + ...eson-add-Dlog-trace-to-set-LOG_TRACE.patch | 50 + ...ding-resolved-and-machined-without-n.patch | 235 ++ ...0157-meson-drop-duplicated-condition.patch | 33 + ...eson-use-.source_root-in-more-places.patch | 81 + ...n-treat-all-fuzz-cases-as-unit-tests.patch | 141 + ...ssage-add-fuzzer-for-message-parsing.patch | 102 + ...structured-initialization-to-avoid-u.patch | 115 + ...d-an-infinite-loop-on-empty-structur.patch | 176 + ...s-always-use-EBADMSG-when-the-messag.patch | 41 + ...-message-rename-function-for-clarity.patch | 210 + SOURCES/0165-bus-message-use-define.patch | 25 + ...-null-if-the-message-has-unknown-typ.patch | 33 + ...age-fix-calculation-of-offsets-table.patch | 126 + ...-message-remove-duplicate-assignment.patch | 24 + ...calculation-of-offsets-table-for-arr.patch | 87 + ...-asserts-in-functions-which-are-wrap.patch | 43 + ...ut-debug-information-about-offset-tr.patch | 29 + ...skipping-of-array-fields-in-gvariant.patch | 59 + ...-properly-copy-struct-signature-when.patch | 33 + ...age-add-two-test-cases-that-pass-now.patch | 39 + ...rn-EBADMSG-not-EINVAL-on-invalid-gva.patch | 41 + ...d-wrap-around-when-using-length-read.patch | 101 + ...stack-frame-for-parsing-arbitrary-in.patch | 66 + ...ravis-enable-ASan-and-UBSan-on-RHEL8.patch | 213 + ...p-SYS_PTRACE-when-running-under-ASan.patch | 31 + ...various-ubsan-zero-size-memory-fixes.patch | 60 + SOURCES/0181-util-introduce-memcmp_safe.patch | 27 + ...l-avoid-memleak-reported-by-valgrind.patch | 49 + ...ape-binary-data-in-match_make_string.patch | 57 + ...duce-CAP_TO_MASK_CORRECTED-macro-rep.patch | 46 + ...e_t-when-dealing-with-memory-offsets.patch | 25 + ...ll-cap_last_cap-only-once-in-has_cap.patch | 40 + ...t-honour-AT_SYMLINK_FOLLOW-correctly.patch | 26 + ...-travis-switch-from-trusty-to-xenial.patch | 24 + ...-Add-tests-for-receive_fd_iov-and-fr.patch | 262 ++ ...oduce-send_one_fd_iov-and-receive_on.patch | 289 ++ ...of-n_storage_fds-and-n_socket_fds-pa.patch | 184 + ...-usual-syntax-for-defining-bit-masks.patch | 31 + ...introduce-new-Type-exec-service-type.patch | 572 +++ ...-man-document-the-new-Type-exec-type.patch | 207 + ...necting-to-the-pseudo-container-.hos.patch | 57 + ...-also-make-sd-login-understand-.host.patch | 56 + .../0197-test-add-test-for-Type-exec.patch | 109 + ...y-explicitly-declare-local-variables.patch | 75 + SOURCES/0199-tools-drop-unused-variable.patch | 24 + ...use-localStorage-cursor-only-when-it.patch | 36 + ...201-sd-bus-deal-with-cookie-overruns.patch | 87 + ...o-not-request-Content-Length-if-Tran.patch | 77 + ...emove-multiple-spaces-after-identifi.patch | 79 + ...t-fallback-to-PLAIN-mapping-if-LUKS-.patch | 62 + ...p-call-crypt_load-for-LUKS-only-once.patch | 79 + ...6-cryptsetup-Add-LUKS2-token-support.patch | 45 + ...-incorrect-page-length-when-get-devi.patch | 30 + ...of-manager-triggered-restarts-to-JOB.patch | 55 + ...-completion-analyze-support-security.patch | 58 + ...urnal-does-not-validate-syslog-field.patch | 28 + ...1-rules-skip-memory-hotplug-on-ppc64.patch | 22 + ...simplify-proc-self-mountinfo-handler.patch | 86 + ...c-self-mountinfo-before-processing-w.patch | 90 + ...waps-before-processing-waitid-result.patch | 69 + ...urity-fix-potential-division-by-zero.patch | 25 + ...gate-reload-failure-to-service-resul.patch | 26 + ...an-document-systemd-analyze-security.patch | 59 + ...nd-add-examples-to-systemd-analyze-1.patch | 772 ++++ ...ravis-move-to-CentOS-8-docker-images.patch | 132 + SOURCES/0220-travis-drop-SCL-remains.patch | 50 + ...ix-segfault-in-syslog_parse_priority.patch | 110 + ...0222-sd-bus-make-strict-asan-shut-up.patch | 45 + ...on-t-run-slow-tests-under-ASan-UBSan.patch | 43 + ...o-not-require-non-empty-kernel-cmdli.patch | 67 + ...vent-buffer-overrow-when-reading-fro.patch | 36 + ...en-dev-kmsg-again-right-after-mounti.patch | 37 + ...arbage-collect-sections-while-linkin.patch | 29 + .../0228-udev-introduce-CONST-key-name.patch | 186 + ...o-know-size-of-supplementary-groups-.patch | 50 + ...0-Consider-smb3-as-remote-filesystem.patch | 30 + ...til-introduce-pid_is_my_child-helper.patch | 114 + ...number-of-stalled-PIDs-from-the-watc.patch | 323 ++ ...processes-when-it-s-really-necessary.patch | 55 + ...ement-per-unit-journal-rate-limiting.patch | 641 +++ ...ng-path-specs-once-we-triggered-the-.patch | 35 + ...ssertion-failure-when-system-journal.patch | 28 + ...KDF2-instead-of-Argon2-in-cryptsetup.patch | 29 + ...st-mask-several-unnecessary-services.patch | 252 ++ ...p-the-second-partition-s-size-to-50M.patch | 30 + ...fig-exclude-zram-devices-from-hibern.patch | 51 + ...g-SELINUX_INFO-and-SELINUX_WARNING-m.patch | 45 + ...-device-introduce-log_device_-macros.patch | 47 + ...ram-and-rule-for-FIDO-security-token.patch | 505 +++ ...drop-trusted-annotation-from-bus_ope.patch | 33 + ...d-bus-adjust-indentation-of-comments.patch | 50 + .../0246-resolved-do-not-run-loop-twice.patch | 46 + ...ccess-to-Set-Link-and-Revert-methods.patch | 347 ++ ...y-polkit-only-after-parsing-the-data.patch | 48 + ..._cleanup_free_-to-free-a-temporary-s.patch | 48 + ...c-user-util-allow-dots-in-user-names.patch | 90 + ...sd-bus-bump-message-queue-size-again.patch | 29 + ...ournald_processing_function-in-a-.c-.patch | 109 + ...sts-add-a-fuzzer-for-dev_kmsg_record.patch | 129 + ...move-an-assertion-from-cunescape_one.patch | 30 + ...-off-by-one-error-in-dev_kmsg_record.patch | 25 + ...oducer-for-a-memory-leak-fixed-in-30.patch | 25 + ...oducer-for-a-heap-buffer-overflow-fi.patch | 25 + ...-syslog_fd-in-fuzz-journald-kmsg-too.patch | 44 + ...dd-a-fuzzer-for-process_audit_string.patch | 99 + ...hether-sscanf-has-changed-the-value-.patch | 47 + ...dummy_server_init-and-use-it-in-all-.patch | 172 + ...ts-add-a-fuzzer-for-journald-streams.patch | 148 + ...uzzer-for-server_process_native_file.patch | 96 + ...eam-avoid-assertion-failure-on-sampl.patch | 47 + ...ading-spaces-into-account-in-syslog_.patch | 45 + ...out-the-difference-in-permissions-be.patch | 43 + ...emove-one-redundant-comparison-check.patch | 32 + ...rship-mode-of-the-execution-director.patch | 88 + ...te-remove-unnecessary-initialization.patch | 25 + ...til-move-the-part-to-print-cpu-set-i.patch | 215 + ...til-remove-now-unused-CPU_SIZE_TO_NU.patch | 29 + .../0272-Rework-cpu-affinity-parsing.patch | 932 +++++ ...in_affinity_mask-to-cpu-set-util.-ch.patch | 125 + ...l-add-simple-test-for-cpus_in_affini.patch | 40 + ...l-add-a-smoke-test-for-test_parse_cp.patch | 63 + ...e-CPUAffinity-in-incremental-fashion.patch | 148 + ...-setting-from-proc-cmdline-upon-rest.patch | 86 + ...ing-configuration-forget-old-setting.patch | 208 + .../0279-test-execute-use-CPUSet-too.patch | 114 + ...til-drop-now-unused-cleanup-function.patch | 26 + ...til-make-transfer-of-cpu_set_t-over-.patch | 126 + ...t-util-add-test-for-dbus-conversions.patch | 61 + ...-set-util-introduce-cpu_set_to_range.patch | 203 + ...t-CPUAffinity-mask-as-a-list-of-CPU-.patch | 53 + ...til-only-force-range-printing-one-ti.patch | 77 + ...Affinity-as-a-range-string-instead-o.patch | 36 + ...-d-d-format-in-cpu_set_to_range_stri.patch | 95 + ...duce-NUMAPolicy-and-NUMAMask-options.patch | 779 ++++ ...ore-disable-CPUAccounting-by-default.patch | 25 + SOURCES/0290-set-kptr_restrict-1.patch | 24 + ...e-the-chance-that-we-will-be-OOM-kil.patch | 34 + ...akage-of-ordering-dependencies-by-sy.patch | 136 + ...enable-custom-systemd.debug_shell-tt.patch | 149 + ...l-fix-comparison-for-allocation-size.patch | 118 + ...il-fix-allocation-size-check-on-i386.patch | 30 + .../0296-catalog-fix-name-of-variable.patch | 531 +++ ...eyfile-timeout-to-allow-a-keydev-tim.patch | 273 ++ ...dd-documentation-for-keyfile-timeout.patch | 44 + ...up-use-unabbrieviated-variable-names.patch | 63 + ...-assert-on-variable-which-is-optiona.patch | 37 + ...ator-guess-whether-the-keyfile-argum.patch | 100 + ...late-libcryptsetup-log-level-instead.patch | 46 + ...ome-commenting-about-EAGAIN-generati.patch | 25 + ...up-downgrade-a-log-message-we-ignore.patch | 25 + ...k-how-we-log-about-activation-failur.patch | 102 + ...ules-reintroduce-60-alias-kmsg.rules.patch | 41 + ...e-rqueue-wqueue-sizes-of-type-size_t.patch | 49 + ...bus-ref-and-bus-message-ref-handling.patch | 51 + ...-dispatch_rqueue-initializes-return-.patch | 32 + ...s-drop-two-inappropriate-empty-lines.patch | 31 + ...e-mutex-after-we-allocated-the-wqueu.patch | 33 + ...-through-sd_bus_unref-to-free-messag.patch | 74 + ...oduce-two-kinds-of-references-to-bus.patch | 182 + ...-API-for-re-enqueuing-incoming-messa.patch | 74 + ...sd_event_source_disable_unref-helper.patch | 130 + ...orizing-via-PK-let-s-re-resolve-call.patch | 156 + ...default-increase-the-numeric-PID-ran.patch | 70 + ...rigger-assertion-when-journal_file_c.patch | 49 + ...-cleanup-attribute-at-one-more-place.patch | 58 + ...-message-references-for-managing-r-w.patch | 183 + ...o-restore-correct-default-values-for.patch | 261 ++ ...-define-HIGH_RLIMIT_MEMLOCK-similar-.patch | 37 + ...e-seccomp_restrict_suid_sgid-for-blo.patch | 178 + ...add-test-case-for-restrict_suid_sgid.patch | 265 ++ ...-SGID-restriction-as-new-unit-settin.patch | 157 + ...r-RestrictSUIDSGID-in-systemd-analyz.patch | 52 + ...ent-the-new-RestrictSUIDSGID-setting.patch | 83 + ...strictSUIDSGID-in-most-of-our-long-r.patch | 157 + ...nd-SUID-SGID-restriction-for-Dynamic.patch | 89 + ...-support-for-cgroup-v2-CPUSET-contro.patch | 555 +++ ...1-fix-DefaultTasksMax-initialization.patch | 38 + ...-that-cpuset-is-supported-on-cgroup-.patch | 40 + ...33-test-introduce-TEST-36-NUMAPOLICY.patch | 380 ++ ...l-f-with-journal-cursor-which-should.patch | 52 + ...L_LOCAL-matching-in-unpatched-strace.patch | 58 + ...re-the-strace-process-is-indeed-dead.patch | 50 + ...test-on-systems-without-NUMA-support.patch | 36 + ...-give-strace-some-time-to-initialize.patch | 30 + ...e-sanity-check-for-systems-without-N.patch | 391 ++ ...st-drop-the-missed-exit-1-expression.patch | 35 + ...lace-cursor-file-with-a-plain-cursor.patch | 59 + ...-key-file-errors-as-a-failed-passwor.patch | 32 + ...secondary-swap-units-jobs-if-deactiv.patch | 79 + ...-missing-PrivateTmp-yes-and-ProtectS.patch | 41 + ...sd_event_source-when-udevadm-trigger.patch | 30 + ...6-core-rework-StopWhenUnneeded-logic.patch | 339 ++ ...es-of-AllowedCPUs-and-AllowedMemoryN.patch | 81 + ...ix-re-realization-of-cgroup-siblings.patch | 62 + ...as-separator-in-cpuset-cgroup-cpu-ra.patch | 99 + ...to-FINAL_SIGTERM-state-after-ExecSto.patch | 159 + ...-journal-files-that-were-deleted-by-.patch | 75 + ...e-the-dead-code-and-actually-fix-146.patch | 42 + ...essage-when-we-fail-to-set-inotify-w.patch | 43 + ...-PolicyKit-before-allowing-VT-switch.patch | 195 + ...ot-use-global-variable-to-pass-error.patch | 58 + ...-install-libraries-required-by-tests.patch | 25 + ...0357-test-introduce-install_zoneinfo.patch | 32 + ...duplicated-Makefile-by-symbolic-link.patch | 151 + ...-paths-of-keymaps-in-install_keymaps.patch | 34 + ...l_keymaps-optionally-install-more-ke.patch | 33 + ...p-some-tests-when-running-in-unprivi.patch | 47 + ...l-skip-several-verifications-when-ru.patch | 39 + ...so-check-python3-is-installed-or-not.patch | 59 + ...p-several-tests-when-running-in-cont.patch | 42 + ...roduce-test_is_running_from_builddir.patch | 70 + ...6-test-make-test-catalog-relocatable.patch | 103 + ...allelize-tasks-in-TEST-24-UNIT-TESTS.patch | 137 + ...ry-to-determine-QEMU_SMP-dynamically.patch | 41 + ...0369-test-store-coredumps-in-journal.patch | 31 + ...nel-cmdline-arg-systemd.cpu_affinity.patch | 62 + ...tape-changers-also-apprear-in-dev-ta.patch | 57 + ...hat-.timer-time-expressions-need-to-.patch | 75 + ...port-for-opening-files-for-appending.patch | 312 ++ ...oad-to-sub-cgroup-first-then-sync-cg.patch | 36 + ...-legacy-hierarchy-when-it-s-used-in-.patch | 31 + ...tatus_emit_starting_stopping_reloadi.patch | 354 ++ ...as-skipped-due-to-a-failed-condition.patch | 63 + ...ll-logic-that-updates-a-Job-on-a-uni.patch | 169 + ...ssages-about-units-entering-a-failed.patch | 200 + ...nizable-message-when-a-unit-succeeds.patch | 210 + ...s-use-the-right-vtable-wrapper-calls.patch | 113 + ...llow-filtering-test-cases-by-pattern.patch | 153 + ...ecute-provide-custom-failure-message.patch | 568 +++ ...0384-core-ExecCondition-for-services.patch | 738 ++++ SOURCES/0385-Drop-support-for-lz4-1.3.0.patch | 75 + ...d-test-for-short-decompress_startswi.patch | 72 + ...r-new-improved-LZ4_decompress_safe_p.patch | 180 + ...d-fuzzer-for-compression-and-decompr.patch | 118 + .../0389-seccomp-fix-__NR__sysctl-usage.patch | 35 + ...sh-with-NULL-in-arg_root-and-other-f.patch | 174 + ...e-force-if-SYSTEMD_SULOGIN_FORCE-set.patch | 85 + ...ixes-for-the-compatibility-interface.patch | 62 + ...unt-don-t-add-Requires-for-tmp.mount.patch | 26 + .../0394-core-coldplug-possible-nop_job.patch | 55 + ...95-core-add-IODeviceLatencyTargetSec.patch | 584 +++ ...til-Introduce-parse_sec_def_infinity.patch | 122 + ...cgroup-use-structured-initialization.patch | 63 + SOURCES/0398-core-add-CPUQuotaPeriodSec.patch | 403 ++ ...PUQuotaPeriodSec-clamping-logs-to-de.patch | 66 + ...ic-number-in-SASL-length-calculation.patch | 41 + ...-sd-bus-fix-SASL-reply-to-empty-AUTH.patch | 55 + ...skip-sending-formatted-UIDs-via-SASL.patch | 210 + SOURCES/0403-core-add-MemoryMin.patch | 231 ++ ...re-introduce-cgroup_add_device_allow.patch | 98 + ...pport-for-suffix-in-get_testdata_dir.patch | 302 ++ ...-default-propagation-of-MemoryLow-wi.patch | 695 ++++ ...e-UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP.patch | 80 + SOURCES/0408-unit-Add-DefaultMemoryMin.patch | 141 + ...erarchically-aware-protection-docs-a.patch | 56 + ...d-some-plumbing-for-DefaultMemoryMin.patch | 68 + ...-value-for-memory-protection-directi.patch | 86 + ...-it-s-possible-to-set-memory-protect.patch | 58 + ...estor-memory-min-for-unified-memory-.patch | 28 + ...efaultMemoryMin-when-setting-memory..patch | 31 + ...ry-protections-as-explicitly-set-in-.patch | 108 + ...ing-the-version-string-during-config.patch | 86 + ...der-SERVICE_SKIP_CONDITION-for-abnor.patch | 170 + ...ocessor-check-only-in-selinux-access.patch | 39 + ...l-introduce-cg_get_keyed_attribute_f.patch | 144 + ...ic-logic-for-waiting-for-a-unit-to-e.patch | 526 +++ SOURCES/0421-shared-fix-assert-call.patch | 27 + ...-calling-NULL-callback-in-bus_wait_f.patch | 28 + ...ULL-callback-check-in-one-more-place.patch | 77 + ...introduce-support-for-cgroup-freezer.patch | 1146 ++++++ ...return-value-of-unit_cgorup_freezer_.patch | 31 + ...urn-value-in-order-to-make-sure-we-d.patch | 31 + ...d-test-for-cgroup-v2-freezer-support.patch | 397 ++ SOURCES/0428-fix-mis-merge.patch | 29 + ...t-and-give-kernel-time-to-perform-th.patch | 36 + ...-we-emit-PropertiesChanged-signal-on.patch | 26 + ...n-t-emit-PropetiesChanged-needlessly.patch | 44 + ...its-add-generic-boot-complete.target.patch | 46 + ...cument-new-boot-complete.target-unit.patch | 53 + ...o-restore-the-control-command-id-too.patch | 30 + ...ction-must-be-NOP-when-cgroup-v2-fre.patch | 48 + ...nt-warning-when-user-.service-templa.patch | 32 + ...e-project-version-in-pkgconfig-files.patch | 80 + ...-the-proc-1-sched-hack-also-for-PID1.patch | 57 + ...-how-the-S-UG-ID-filter-is-installed.patch | 287 ++ ...owngrade-log-message-when-setting-fo.patch | 91 + ...d.special-man-page-reference-in-syst.patch | 26 + ...s-drop-reference-to-sushell-man-page.patch | 27 + ...-loop-in-bus_ensure_running-if-the-b.patch | 44 + ...-for-enqueing-a-job-with-returning-t.patch | 831 ++++ ...e-switch-statement-by-table-of-struc.patch | 115 + SOURCES/0446-systemctl-reindent-table.patch | 53 + ...ait-when-there-s-something-to-wait-f.patch | 29 + ...ean-up-start_unit_one-error-handling.patch | 99 + ...out-extra-args-generation-into-helpe.patch | 68 + ...mctl-add-new-show-transaction-switch.patch | 362 ++ ...sic-testing-that-systemctl-start-T-d.patch | 31 + ...-new-systemctl-show-transaction-opti.patch | 37 + ...n-FlushPending-boolean-to-flush-sock.patch | 153 + ...ort-for-API-bus-started-outside-our-.patch | 62 + ...segfault-in-mount_cgroup_controllers.patch | 56 + ...e-transfer-of-CPUAffinity-endian-saf.patch | 39 + ...-for-setting-CPUAffinity-to-special-.patch | 426 ++ ...always-use-base-10-for-user-group-nu.patch | 93 + ...imes-it-is-useful-to-check-if-a-stri.patch | 149 + ...60-basic-parse-util-add-safe_atoux64.patch | 130 + ...allow-tweaking-how-to-parse-integers.patch | 137 + ...il-allow-0-as-alternative-to-0-and-0.patch | 60 + ...return-parameter-optional-in-safe_at.patch | 31 + ...te-parse_mode-on-top-of-safe_atou_fu.patch | 59 + ...5-user-util-be-stricter-in-parse_uid.patch | 78 + ...66-strv-add-new-macro-STARTSWITH_SET.patch | 75 + ...parse-integers-prefixed-with-0b-and-.patch | 164 + ...-tests-beef-up-integer-parsing-tests.patch | 204 + ...-add-compat-forms-of-user-name-check.patch | 270 ++ ...il-emit-a-warning-on-names-with-dots.patch | 50 + ...il-Allow-names-starting-with-a-digit.patch | 103 + ...-allow-usernames-with-dots-in-specif.patch | 193 + ...-order-of-checks-in-valid_user_group.patch | 38 + ...il-rework-how-we-validate-user-names.patch | 803 ++++ ...em-Administrator-s-Guide-in-systemct.patch | 35 + ...introduce-udev-net_id-naming-schemes.patch | 257 ++ ...t.naming-scheme-default-configurable.patch | 188 + ...ibe-naming-schemes-in-a-new-man-page.patch | 522 +++ ...e-_SUN-ACPI-index-as-a-signed-intege.patch | 51 + ...t-generate-slot-based-names-if-multi.patch | 124 + ...481-fix-typo-in-ProtectSystem-option.patch | 25 + ...references-of-non-existent-man-pages.patch | 34 + ...ng-to-CLI-unless-JOURNAL_STREAM-is-s.patch | 91 + ...l-add-new-helper-locale_is_installed.patch | 58 + ...dd-test-case-for-locale_is_installed.patch | 53 + ...arious-bits-over-to-locale_is_instal.patch | 232 ++ ...stantiated-units-to-be-enabled-via-p.patch | 339 ++ ...factor-to-combine-two-function-calls.patch | 127 + SOURCES/0489-test-fix-a-memleak.patch | 28 + ...for-templated-units-to-systemd.prese.patch | 55 + ...ix-preset-operations-for-non-service.patch | 45 + ...0492-introduce-setsockopt_int-helper.patch | 30 + ...d-generic-socket_pass_pktinfo-helper.patch | 57 + ...-PassPacketInfo-socket-unit-property.patch | 156 + ...0495-resolved-tweak-cmsg-calculation.patch | 30 + ...po-was-renamed-to-powertools-in-RHEL.patch | 26 + ...nstead-of-Docker-Hub-to-avoid-rate-l.patch | 33 + ...ve-jobs-from-Travis-CI-to-GH-Actions.patch | 338 ++ ...ast-function-deal-with-NULL-pointers.patch | 31 + SOURCES/0500-use-link-to-RHEL-8-docs.patch | 25 + ...501-cgroup-Also-set-blkio.bfq.weight.patch | 37 + ...initrd-cleanup.service-terminates-be.patch | 37 + ...SELinux-label-cache-on-daemon-reload.patch | 73 + ...e-mac_selinux_create_file_prepare_at.patch | 140 + ...ger-for-policy-reload-to-refresh-int.patch | 135 + ...d-give-RHEL-8.4-naming-scheme-a-name.patch | 24 + ...make-mtime-check-stricter-and-use-en.patch | 63 + ...thm-that-selects-highest-priority-de.patch | 457 +++ ...test-create-dev-null-in-test-udev.pl.patch | 32 + SOURCES/0510-test-missing-die.patch | 27 + ...-a-check-for-whether-the-test-is-run.patch | 33 + ...he-test-only-if-it-can-t-setup-its-e.patch | 94 + ...13-udev-test-fix-test-skip-condition.patch | 33 + ...-test-fix-missing-directory-test-run.patch | 35 + ...if-permitted-to-create-block-device-.patch | 31 + ...udev-add-a-testcase-of-too-long-line.patch | 45 + ...oper-semantics-for-too-long-line-wit.patch | 34 + ...re-tests-for-line-continuations-and-.patch | 40 + ...add-more-tests-for-line-continuation.patch | 59 + ...ignment-and-drop-unnecessary-white-s.patch | 501 +++ ...dev-test.pl-cleanup-if-skipping-test.patch | 73 + ...dd-test-cases-for-empty-string-match.patch | 89 + ...test-case-for-multi-matches-when-use.patch | 35 + ...not-rely-on-mail-group-being-defined.patch | 33 + ...t.pl-allow-multiple-devices-per-test.patch | 2452 ++++++++++++ ...-udev-test.pl-create-rules-only-once.patch | 61 + ...l-allow-concurrent-additions-and-rem.patch | 169 + ...ev-test.pl-use-computed-devnode-name.patch | 260 ++ ...l-test-correctness-of-symlink-target.patch | 61 + ....pl-allow-checking-multiple-symlinks.patch | 1607 ++++++++ ...-test.pl-fix-wrong-test-descriptions.patch | 83 + ...dev-test.pl-last_rule-is-unsupported.patch | 35 + ...t.pl-Make-some-tests-a-little-harder.patch | 74 + ...l-remove-bogus-rules-from-magic-subs.patch | 28 + ...l-merge-space-and-var-with-space-tes.patch | 57 + ...l-merge-import-parent-tests-into-one.patch | 53 + ...test-udev-test.pl-count-good-results.patch | 136 + ...dev-test.pl-add-multiple-device-test.patch | 199 + ...9-test-udev-test.pl-add-repeat-count.patch | 44 + ...l-generator-for-large-list-of-block-.patch | 101 + ...l-suppress-umount-error-message-at-s.patch | 29 + ...udev_test.pl-add-expected-good-count.patch | 78 + ...st-gracefully-exit-when-imports-fail.patch | 48 + ...test-cases-for-empty-string-match-an.patch | 123 + ...py-add-missing-DEVNAME-entries-to-ue.patch | 70 + ...ut-helper-functions-for-reshuffling-.patch | 207 + ...ut-enable-and-disable-codepaths-from.patch | 306 ++ ...-that-two-debug-logged-events-are-ig.patch | 37 + ...lock-data-allocation-out-of-sd_event.patch | 71 + ...ut-code-to-add-remove-timer-event-so.patch | 112 + ...vent-fix-delays-assert-brain-o-17790.patch | 29 + ...-suffix-last_run-last_log-with-_usec.patch | 60 + ...running-default-event-loops-in-any-o.patch | 42 + ...nt-loop-while-in-sd_event_prepare-ot.patch | 92 + ...coding-style-with-naming-return-para.patch | 35 + ...earliest_index-latest_index-into-com.patch | 98 + ...state-at-the-end-in-event_source_ena.patch | 116 + ...se-n_enabled_child_sources-just-once.patch | 36 + ...d-ability-to-ratelimit-event-sources.patch | 858 ++++ SOURCES/0560-test-add-ratelimiting-test.patch | 127 + ...xcessive-proc-self-mountinfo-parsing.patch | 29 + ...date-with-increased-retry-count-in-s.patch | 44 + ...use-secure_getenv-rather-than-getenv.patch | 89 + SOURCES/20-grubby.install | 58 + SOURCES/20-yama-ptrace.conf | 42 + SOURCES/inittab | 16 + SOURCES/purge-nobody-user | 101 + SOURCES/rc.local | 14 + SOURCES/split-files.py | 116 + SOURCES/sysctl.conf.README | 10 + SOURCES/systemd-journal-gatewayd.xml | 6 + SOURCES/systemd-journal-remote.xml | 6 + SOURCES/systemd-udev-trigger-no-reload.conf | 3 + SOURCES/systemd-user | 10 + SOURCES/triggers.systemd | 109 + SOURCES/yum-protect-systemd.conf | 2 + SPECS/systemd.spec | 3509 +++++++++++++++++ 579 files changed, 71518 insertions(+) create mode 100644 .gitignore create mode 100644 .systemd.metadata create mode 100644 SOURCES/0001-build-sys-Detect-whether-struct-statx-is-defined-in-.patch create mode 100644 SOURCES/0002-logind-set-RemoveIPC-to-false-by-default.patch create mode 100644 SOURCES/0003-pid1-bump-DefaultTasksMax-to-80-of-the-kernel-pid.ma.patch create mode 100644 SOURCES/0004-Avoid-tmp-being-mounted-as-tmpfs-without-the-user-s-.patch create mode 100644 SOURCES/0005-pid1-bump-maximum-number-of-process-in-user-slice-to.patch create mode 100644 SOURCES/0006-rules-automatically-online-hot-plugged-CPUs.patch create mode 100644 SOURCES/0007-rules-add-rule-for-naming-Dell-iDRAC-USB-Virtual-NIC.patch create mode 100644 SOURCES/0008-rules-enable-memory-hotplug.patch create mode 100644 SOURCES/0009-rules-reload-sysctl-settings-when-the-bridge-module-.patch create mode 100644 SOURCES/0010-rules-load-sg-module.patch create mode 100644 SOURCES/0011-rules-prandom-character-device-node-permissions.patch create mode 100644 SOURCES/0012-rules-load-sg-driver-also-when-scsi_target-appears-4.patch create mode 100644 SOURCES/0013-rules-don-t-hoplug-memory-on-s390x.patch create mode 100644 SOURCES/0014-rules-disable-auto-online-of-hot-plugged-memory-on-I.patch create mode 100644 SOURCES/0015-rules-introduce-old-style-by-path-symlinks-for-FCP-b.patch create mode 100644 SOURCES/0016-Revert-udev-remove-WAIT_FOR-key.patch create mode 100644 SOURCES/0017-net_setup_link-allow-renaming-interfaces-that-were-r.patch create mode 100644 SOURCES/0018-units-drop-DynamicUser-yes-from-systemd-resolved.ser.patch create mode 100644 SOURCES/0019-journal-remove-journal-audit-socket.patch create mode 100644 SOURCES/0020-bus-move-BUS_DONT_DESTROY-calls-after-asserts.patch create mode 100644 SOURCES/0021-random-seed-raise-POOL_SIZE_MIN-constant-to-1024.patch create mode 100644 SOURCES/0022-cryptsetup-add-support-for-sector-size-option-9936.patch create mode 100644 SOURCES/0023-cryptsetup-do-not-define-arg_sector_size-if-libgcryp.patch create mode 100644 SOURCES/0024-units-don-t-enable-per-service-IP-firewall-by-defaul.patch create mode 100644 SOURCES/0025-bus-message-do-not-crash-on-message-with-a-string-of.patch create mode 100644 SOURCES/0026-Introduce-free_and_strndup-and-use-it-in-bus-message.patch create mode 100644 SOURCES/0027-tests-backport-test_setup_logging.patch create mode 100644 SOURCES/0028-journal-change-support-URL-shown-in-the-catalog-entr.patch create mode 100644 SOURCES/0029-resolved-create-etc-resolv.conf-symlink-at-runtime.patch create mode 100644 SOURCES/0030-dissect-image-use-right-comparison-function.patch create mode 100644 SOURCES/0031-login-avoid-leak-of-name-returned-by-uid_to_name.patch create mode 100644 SOURCES/0032-firewall-util-add-an-assert-that-we-re-not-overwriti.patch create mode 100644 SOURCES/0033-journal-file-avoid-calling-ftruncate-with-invalid-fd.patch create mode 100644 SOURCES/0034-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch create mode 100644 SOURCES/0035-core-rename-queued_message-pending_reload_message.patch create mode 100644 SOURCES/0036-core-when-we-can-t-send-the-pending-reload-message-s.patch create mode 100644 SOURCES/0037-core-make-sure-we-don-t-throttle-change-signal-gener.patch create mode 100644 SOURCES/0038-proc-cmdline-introduce-PROC_CMDLINE_RD_STRICT.patch create mode 100644 SOURCES/0039-debug-generator-introduce-rd.-version-of-all-options.patch create mode 100644 SOURCES/0040-chown-recursive-let-s-rework-the-recursive-logic-to-.patch create mode 100644 SOURCES/0041-chown-recursive-also-drop-ACLs-when-recursively-chow.patch create mode 100644 SOURCES/0042-chown-recursive-TAKE_FD-is-your-friend.patch create mode 100644 SOURCES/0043-test-add-test-case-for-recursive-chown-ing.patch create mode 100644 SOURCES/0044-Revert-sysctl.d-request-ECN-on-both-in-and-outgoing-.patch create mode 100644 SOURCES/0045-detect-virt-do-not-try-to-read-all-of-proc-cpuinfo.patch create mode 100644 SOURCES/0046-sd-bus-unify-three-code-paths-which-free-struct-bus_.patch create mode 100644 SOURCES/0047-sd-bus-properly-initialize-containers.patch create mode 100644 SOURCES/0048-cryptsetup-generator-introduce-basic-keydev-support.patch create mode 100644 SOURCES/0049-cryptsetup-don-t-use-m-if-there-s-no-error-to-show.patch create mode 100644 SOURCES/0050-cryptsetup-generator-don-t-return-error-if-target-di.patch create mode 100644 SOURCES/0051-cryptsetup-generator-allow-whitespace-characters-in-.patch create mode 100644 SOURCES/0052-rules-watch-metadata-changes-on-DASD-devices.patch create mode 100644 SOURCES/0053-sysctl.d-switch-net.ipv4.conf.all.rp_filter-from-1-t.patch create mode 100644 SOURCES/0054-tests-explicitly-enable-user-namespaces-for-TEST-13-.patch create mode 100644 SOURCES/0055-nspawn-beef-up-netns-checking-a-bit-for-compat-with-.patch create mode 100644 SOURCES/0056-test-Drop-SKIP_INITRD-for-QEMU-based-tests.patch create mode 100644 SOURCES/0057-meson-rename-Ddebug-to-Ddebug-extra.patch create mode 100644 SOURCES/0058-meson-check-whether-gnutls-supports-TCP-fast-open.patch create mode 100644 SOURCES/0059-unit-don-t-add-Requires-for-tmp.mount.patch create mode 100644 SOURCES/0060-tests-drop-the-precondition-check-for-inherited-flag.patch create mode 100644 SOURCES/0061-core-when-deserializing-state-always-use-read_line-L.patch create mode 100644 SOURCES/0062-core-enforce-a-limit-on-STATUS-texts-recvd-from-serv.patch create mode 100644 SOURCES/0063-travis-enable-Travis-CI-on-CentOS-7.patch create mode 100644 SOURCES/0064-travis-RHEL8-support.patch create mode 100644 SOURCES/0065-travis-drop-the-SELinux-Fedora-workaround.patch create mode 100644 SOURCES/0066-travis-fix-syntax-error-in-.travis.yml.patch create mode 100644 SOURCES/0067-travis-reboot-the-container-before-running-tests.patch create mode 100644 SOURCES/0068-coredump-remove-duplicate-MESSAGE-prefix-from-messag.patch create mode 100644 SOURCES/0069-journald-remove-unnecessary.patch create mode 100644 SOURCES/0070-journald-do-not-store-the-iovec-entry-for-process-co.patch create mode 100644 SOURCES/0071-basic-process-util-limit-command-line-lengths-to-_SC.patch create mode 100644 SOURCES/0072-coredump-fix-message-when-we-fail-to-save-a-journald.patch create mode 100644 SOURCES/0073-procfs-util-expose-functionality-to-query-total-memo.patch create mode 100644 SOURCES/0074-basic-prioq-add-prioq_peek_item.patch create mode 100644 SOURCES/0075-journal-limit-the-number-of-entries-in-the-cache-bas.patch create mode 100644 SOURCES/0076-journald-periodically-drop-cache-for-all-dead-PIDs.patch create mode 100644 SOURCES/0077-process-util-don-t-use-overly-large-buffer-to-store-.patch create mode 100644 SOURCES/0078-Revert-sysctl.d-switch-net.ipv4.conf.all.rp_filter-f.patch create mode 100644 SOURCES/0079-journal-fix-syslog_parse_identifier.patch create mode 100644 SOURCES/0080-journald-set-a-limit-on-the-number-of-fields-1k.patch create mode 100644 SOURCES/0081-journald-when-processing-a-native-message-bail-more-.patch create mode 100644 SOURCES/0082-journald-lower-the-maximum-entry-size-limit-to-for-n.patch create mode 100644 SOURCES/0083-httpd-use-a-cleanup-function-to-call-MHD_destroy_res.patch create mode 100644 SOURCES/0084-journal-remote-verify-entry-length-from-header.patch create mode 100644 SOURCES/0085-journal-remote-set-a-limit-on-the-number-of-fields-i.patch create mode 100644 SOURCES/0086-journald-correctly-attribute-log-messages-also-with-.patch create mode 100644 SOURCES/0087-test-replace-echo-with-socat.patch create mode 100644 SOURCES/0088-test-network-ignore-tunnel-devices-automatically-add.patch create mode 100644 SOURCES/0089-rules-add-elevator-kernel-command-line-parameter.patch create mode 100644 SOURCES/0090-rule-syntax-check-allow-PROGRAM-as-an-assignment.patch create mode 100644 SOURCES/0091-rules-implement-new-memory-hotplug-policy.patch create mode 100644 SOURCES/0092-LGTM-make-LGTM.com-use-meson-from-pip.patch create mode 100644 SOURCES/0093-lgtm-use-python3.patch create mode 100644 SOURCES/0094-tools-use-print-function-in-Python-3-code.patch create mode 100644 SOURCES/0095-lgtm-add-a-custom-query-for-catching-the-use-of-fget.patch create mode 100644 SOURCES/0096-lgtm-drop-redundant-newlines.patch create mode 100644 SOURCES/0097-rules-add-the-rule-that-adds-elevator-kernel-command.patch create mode 100644 SOURCES/0098-test-add-TEST-24-UNIT-TESTS-running-all-basic-tests-.patch create mode 100644 SOURCES/0099-tests-create-the-asan-wrapper-automatically-if-syste.patch create mode 100644 SOURCES/0100-tests-add-a-wrapper-for-when-systemd-is-built-with-A.patch create mode 100644 SOURCES/0101-tests-redirect-ASAN-reports-on-journald-to-a-file.patch create mode 100644 SOURCES/0102-tests-use-the-asan-wrapper-to-boot-a-VM-container-if.patch create mode 100644 SOURCES/0103-tests-allow-passing-additional-arguments-to-nspawn-v.patch create mode 100644 SOURCES/0104-tests-also-run-TEST-01-BASIC-in-an-unprivileged-cont.patch create mode 100644 SOURCES/0105-test-don-t-overwrite-TESTDIR-if-already-set.patch create mode 100644 SOURCES/0106-bus-socket-Fix-line_begins-to-accept-word-matching-f.patch create mode 100644 SOURCES/0107-Refuse-dbus-message-paths-longer-than-BUS_PATH_SIZE_.patch create mode 100644 SOURCES/0108-Allocate-temporary-strings-to-hold-dbus-paths-on-the.patch create mode 100644 SOURCES/0109-sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch create mode 100644 SOURCES/0110-meson-drop-misplaced-Wl-undefined-argument.patch create mode 100644 SOURCES/0111-Revert-core-one-step-back-again-for-nspawn-we-actual.patch create mode 100644 SOURCES/0112-tree-wide-shorten-error-logging-a-bit.patch create mode 100644 SOURCES/0113-nspawn-simplify-machine-terminate-bus-call.patch create mode 100644 SOURCES/0114-nspawn-merge-two-variable-declaration-lines.patch create mode 100644 SOURCES/0115-nspawn-rework-how-we-allocate-kill-scopes.patch create mode 100644 SOURCES/0116-unit-enqueue-cgroup-empty-check-event-if-the-last-re.patch create mode 100644 SOURCES/0117-Revert-journal-remove-journal-audit-socket.patch create mode 100644 SOURCES/0118-journal-don-t-enable-systemd-journald-audit.socket-b.patch create mode 100644 SOURCES/0119-logs-show-use-grey-color-for-de-emphasizing-journal-.patch create mode 100644 SOURCES/0120-units-add-Install-section-to-tmp.mount.patch create mode 100644 SOURCES/0121-nss-do-not-modify-errno-when-NSS_STATUS_NOTFOUND-or-.patch create mode 100644 SOURCES/0122-util.h-add-new-UNPROTECT_ERRNO-macro.patch create mode 100644 SOURCES/0123-nss-unportect-errno-before-writing-to-NSS-errnop.patch create mode 100644 SOURCES/0124-seccomp-reduce-logging-about-failure-to-add-syscall-.patch create mode 100644 SOURCES/0125-format-table-when-duplicating-a-cell-also-copy-the-c.patch create mode 100644 SOURCES/0126-format-table-optionally-make-specific-cells-clickabl.patch create mode 100644 SOURCES/0127-format-table-before-outputting-a-color-check-if-colo.patch create mode 100644 SOURCES/0128-format-table-add-option-to-store-format-percent-and-.patch create mode 100644 SOURCES/0129-format-table-optionally-allow-reversing-the-sort-ord.patch create mode 100644 SOURCES/0130-format-table-add-table_update-to-update-existing-ent.patch create mode 100644 SOURCES/0131-format-table-add-an-API-for-getting-the-cell-at-a-sp.patch create mode 100644 SOURCES/0132-format-table-always-underline-header-line.patch create mode 100644 SOURCES/0133-format-table-add-calls-to-query-the-data-in-a-specif.patch create mode 100644 SOURCES/0134-format-table-make-sure-we-never-call-memcmp-with-NUL.patch create mode 100644 SOURCES/0135-format-table-use-right-field-for-display.patch create mode 100644 SOURCES/0136-format-table-add-option-to-uppercase-cells-on-displa.patch create mode 100644 SOURCES/0137-format-table-never-try-to-reuse-cells-that-have-colo.patch create mode 100644 SOURCES/0138-locale-util-add-logic-to-output-smiley-emojis-at-var.patch create mode 100644 SOURCES/0139-analyze-add-new-security-verb.patch create mode 100644 SOURCES/0140-tests-add-a-rudimentary-fuzzer-for-server_process_sy.patch create mode 100644 SOURCES/0141-journald-make-it-clear-that-dev_kmsg_record-modifies.patch create mode 100644 SOURCES/0142-journald-free-the-allocated-memory-before-returning-.patch create mode 100644 SOURCES/0143-tests-rework-the-code-fuzzing-journald.patch create mode 100644 SOURCES/0144-journald-make-server_process_native_message-compatib.patch create mode 100644 SOURCES/0145-tests-add-a-fuzzer-for-server_process_native_message.patch create mode 100644 SOURCES/0146-tests-add-a-fuzzer-for-sd-ndisc.patch create mode 100644 SOURCES/0147-ndisc-fix-two-infinite-loops.patch create mode 100644 SOURCES/0148-tests-add-reproducers-for-several-issues-uncovered-w.patch create mode 100644 SOURCES/0149-tests-add-a-reproducer-for-an-infinite-loop-in-ndisc.patch create mode 100644 SOURCES/0150-tests-add-a-reproducer-for-another-infinite-loop-in-.patch create mode 100644 SOURCES/0151-fuzz-rename-fuzz-corpus-directory-to-just-fuzz.patch create mode 100644 SOURCES/0152-test-add-testcase-for-issue-10007-by-oss-fuzz.patch create mode 100644 SOURCES/0153-fuzz-unify-the-fuzz-regressions-directory-with-the-m.patch create mode 100644 SOURCES/0154-test-bus-marshal-use-cescaping-instead-of-hexmem.patch create mode 100644 SOURCES/0155-meson-add-Dlog-trace-to-set-LOG_TRACE.patch create mode 100644 SOURCES/0156-meson-allow-building-resolved-and-machined-without-n.patch create mode 100644 SOURCES/0157-meson-drop-duplicated-condition.patch create mode 100644 SOURCES/0158-meson-use-.source_root-in-more-places.patch create mode 100644 SOURCES/0159-meson-treat-all-fuzz-cases-as-unit-tests.patch create mode 100644 SOURCES/0160-fuzz-bus-message-add-fuzzer-for-message-parsing.patch create mode 100644 SOURCES/0161-bus-message-use-structured-initialization-to-avoid-u.patch create mode 100644 SOURCES/0162-bus-message-avoid-an-infinite-loop-on-empty-structur.patch create mode 100644 SOURCES/0163-bus-message-let-s-always-use-EBADMSG-when-the-messag.patch create mode 100644 SOURCES/0164-bus-message-rename-function-for-clarity.patch create mode 100644 SOURCES/0165-bus-message-use-define.patch create mode 100644 SOURCES/0166-bus-do-not-print-null-if-the-message-has-unknown-typ.patch create mode 100644 SOURCES/0167-bus-message-fix-calculation-of-offsets-table.patch create mode 100644 SOURCES/0168-bus-message-remove-duplicate-assignment.patch create mode 100644 SOURCES/0169-bus-message-fix-calculation-of-offsets-table-for-arr.patch create mode 100644 SOURCES/0170-bus-message-drop-asserts-in-functions-which-are-wrap.patch create mode 100644 SOURCES/0171-bus-message-output-debug-information-about-offset-tr.patch create mode 100644 SOURCES/0172-bus-message-fix-skipping-of-array-fields-in-gvariant.patch create mode 100644 SOURCES/0173-bus-message-also-properly-copy-struct-signature-when.patch create mode 100644 SOURCES/0174-fuzz-bus-message-add-two-test-cases-that-pass-now.patch create mode 100644 SOURCES/0175-bus-message-return-EBADMSG-not-EINVAL-on-invalid-gva.patch create mode 100644 SOURCES/0176-bus-message-avoid-wrap-around-when-using-length-read.patch create mode 100644 SOURCES/0177-util-do-not-use-stack-frame-for-parsing-arbitrary-in.patch create mode 100644 SOURCES/0178-travis-enable-ASan-and-UBSan-on-RHEL8.patch create mode 100644 SOURCES/0179-tests-keep-SYS_PTRACE-when-running-under-ASan.patch create mode 100644 SOURCES/0180-tree-wide-various-ubsan-zero-size-memory-fixes.patch create mode 100644 SOURCES/0181-util-introduce-memcmp_safe.patch create mode 100644 SOURCES/0182-test-socket-util-avoid-memleak-reported-by-valgrind.patch create mode 100644 SOURCES/0183-sd-journal-escape-binary-data-in-match_make_string.patch create mode 100644 SOURCES/0184-capability-introduce-CAP_TO_MASK_CORRECTED-macro-rep.patch create mode 100644 SOURCES/0185-sd-bus-use-size_t-when-dealing-with-memory-offsets.patch create mode 100644 SOURCES/0186-sd-bus-call-cap_last_cap-only-once-in-has_cap.patch create mode 100644 SOURCES/0187-mount-point-honour-AT_SYMLINK_FOLLOW-correctly.patch create mode 100644 SOURCES/0188-travis-switch-from-trusty-to-xenial.patch create mode 100644 SOURCES/0189-test-socket-util-Add-tests-for-receive_fd_iov-and-fr.patch create mode 100644 SOURCES/0190-socket-util-Introduce-send_one_fd_iov-and-receive_on.patch create mode 100644 SOURCES/0191-core-swap-order-of-n_storage_fds-and-n_socket_fds-pa.patch create mode 100644 SOURCES/0192-execute-use-our-usual-syntax-for-defining-bit-masks.patch create mode 100644 SOURCES/0193-core-introduce-new-Type-exec-service-type.patch create mode 100644 SOURCES/0194-man-document-the-new-Type-exec-type.patch create mode 100644 SOURCES/0195-sd-bus-allow-connecting-to-the-pseudo-container-.hos.patch create mode 100644 SOURCES/0196-sd-login-let-s-also-make-sd-login-understand-.host.patch create mode 100644 SOURCES/0197-test-add-test-for-Type-exec.patch create mode 100644 SOURCES/0198-journal-gateway-explicitly-declare-local-variables.patch create mode 100644 SOURCES/0199-tools-drop-unused-variable.patch create mode 100644 SOURCES/0200-journal-gateway-use-localStorage-cursor-only-when-it.patch create mode 100644 SOURCES/0201-sd-bus-deal-with-cookie-overruns.patch create mode 100644 SOURCES/0202-journal-remote-do-not-request-Content-Length-if-Tran.patch create mode 100644 SOURCES/0203-journal-do-not-remove-multiple-spaces-after-identifi.patch create mode 100644 SOURCES/0204-cryptsetup-Do-not-fallback-to-PLAIN-mapping-if-LUKS-.patch create mode 100644 SOURCES/0205-cryptsetup-call-crypt_load-for-LUKS-only-once.patch create mode 100644 SOURCES/0206-cryptsetup-Add-LUKS2-token-support.patch create mode 100644 SOURCES/0207-udev-scsi_id-fix-incorrect-page-length-when-get-devi.patch create mode 100644 SOURCES/0208-Change-job-mode-of-manager-triggered-restarts-to-JOB.patch create mode 100644 SOURCES/0209-bash-completion-analyze-support-security.patch create mode 100644 SOURCES/0210-man-note-that-journal-does-not-validate-syslog-field.patch create mode 100644 SOURCES/0211-rules-skip-memory-hotplug-on-ppc64.patch create mode 100644 SOURCES/0212-mount-simplify-proc-self-mountinfo-handler.patch create mode 100644 SOURCES/0213-mount-rescan-proc-self-mountinfo-before-processing-w.patch create mode 100644 SOURCES/0214-swap-scan-proc-swaps-before-processing-waitid-result.patch create mode 100644 SOURCES/0215-analyze-security-fix-potential-division-by-zero.patch create mode 100644 SOURCES/0216-core-never-propagate-reload-failure-to-service-resul.patch create mode 100644 SOURCES/0217-man-document-systemd-analyze-security.patch create mode 100644 SOURCES/0218-man-reorder-and-add-examples-to-systemd-analyze-1.patch create mode 100644 SOURCES/0219-travis-move-to-CentOS-8-docker-images.patch create mode 100644 SOURCES/0220-travis-drop-SCL-remains.patch create mode 100644 SOURCES/0221-syslog-fix-segfault-in-syslog_parse_priority.patch create mode 100644 SOURCES/0222-sd-bus-make-strict-asan-shut-up.patch create mode 100644 SOURCES/0223-travis-don-t-run-slow-tests-under-ASan-UBSan.patch create mode 100644 SOURCES/0224-kernel-install-do-not-require-non-empty-kernel-cmdli.patch create mode 100644 SOURCES/0225-ask-password-prevent-buffer-overrow-when-reading-fro.patch create mode 100644 SOURCES/0226-core-try-to-reopen-dev-kmsg-again-right-after-mounti.patch create mode 100644 SOURCES/0227-buildsys-don-t-garbage-collect-sections-while-linkin.patch create mode 100644 SOURCES/0228-udev-introduce-CONST-key-name.patch create mode 100644 SOURCES/0229-Call-getgroups-to-know-size-of-supplementary-groups-.patch create mode 100644 SOURCES/0230-Consider-smb3-as-remote-filesystem.patch create mode 100644 SOURCES/0231-process-util-introduce-pid_is_my_child-helper.patch create mode 100644 SOURCES/0232-core-reduce-the-number-of-stalled-PIDs-from-the-watc.patch create mode 100644 SOURCES/0233-core-only-watch-processes-when-it-s-really-necessary.patch create mode 100644 SOURCES/0234-core-implement-per-unit-journal-rate-limiting.patch create mode 100644 SOURCES/0235-path-stop-watching-path-specs-once-we-triggered-the-.patch create mode 100644 SOURCES/0236-journald-fixed-assertion-failure-when-system-journal.patch create mode 100644 SOURCES/0237-test-use-PBKDF2-instead-of-Argon2-in-cryptsetup.patch create mode 100644 SOURCES/0238-test-mask-several-unnecessary-services.patch create mode 100644 SOURCES/0239-test-bump-the-second-partition-s-size-to-50M.patch create mode 100644 SOURCES/0240-shared-sleep-config-exclude-zram-devices-from-hibern.patch create mode 100644 SOURCES/0241-selinux-don-t-log-SELINUX_INFO-and-SELINUX_WARNING-m.patch create mode 100644 SOURCES/0242-sd-device-introduce-log_device_-macros.patch create mode 100644 SOURCES/0243-udev-Add-id-program-and-rule-for-FIDO-security-token.patch create mode 100644 SOURCES/0244-shared-but-util-drop-trusted-annotation-from-bus_ope.patch create mode 100644 SOURCES/0245-sd-bus-adjust-indentation-of-comments.patch create mode 100644 SOURCES/0246-resolved-do-not-run-loop-twice.patch create mode 100644 SOURCES/0247-resolved-allow-access-to-Set-Link-and-Revert-methods.patch create mode 100644 SOURCES/0248-resolved-query-polkit-only-after-parsing-the-data.patch create mode 100644 SOURCES/0249-journal-rely-on-_cleanup_free_-to-free-a-temporary-s.patch create mode 100644 SOURCES/0250-basic-user-util-allow-dots-in-user-names.patch create mode 100644 SOURCES/0251-sd-bus-bump-message-queue-size-again.patch create mode 100644 SOURCES/0252-tests-put-fuzz_journald_processing_function-in-a-.c-.patch create mode 100644 SOURCES/0253-tests-add-a-fuzzer-for-dev_kmsg_record.patch create mode 100644 SOURCES/0254-basic-remove-an-assertion-from-cunescape_one.patch create mode 100644 SOURCES/0255-journal-fix-an-off-by-one-error-in-dev_kmsg_record.patch create mode 100644 SOURCES/0256-tests-add-a-reproducer-for-a-memory-leak-fixed-in-30.patch create mode 100644 SOURCES/0257-tests-add-a-reproducer-for-a-heap-buffer-overflow-fi.patch create mode 100644 SOURCES/0258-test-initialize-syslog_fd-in-fuzz-journald-kmsg-too.patch create mode 100644 SOURCES/0259-tests-add-a-fuzzer-for-process_audit_string.patch create mode 100644 SOURCES/0260-journald-check-whether-sscanf-has-changed-the-value-.patch create mode 100644 SOURCES/0261-tests-introduce-dummy_server_init-and-use-it-in-all-.patch create mode 100644 SOURCES/0262-tests-add-a-fuzzer-for-journald-streams.patch create mode 100644 SOURCES/0263-tests-add-a-fuzzer-for-server_process_native_file.patch create mode 100644 SOURCES/0264-fuzz-journal-stream-avoid-assertion-failure-on-sampl.patch create mode 100644 SOURCES/0265-journald-take-leading-spaces-into-account-in-syslog_.patch create mode 100644 SOURCES/0266-Add-a-warning-about-the-difference-in-permissions-be.patch create mode 100644 SOURCES/0267-execute-remove-one-redundant-comparison-check.patch create mode 100644 SOURCES/0268-core-change-ownership-mode-of-the-execution-director.patch create mode 100644 SOURCES/0269-core-dbus-execute-remove-unnecessary-initialization.patch create mode 100644 SOURCES/0270-shared-cpu-set-util-move-the-part-to-print-cpu-set-i.patch create mode 100644 SOURCES/0271-shared-cpu-set-util-remove-now-unused-CPU_SIZE_TO_NU.patch create mode 100644 SOURCES/0272-Rework-cpu-affinity-parsing.patch create mode 100644 SOURCES/0273-Move-cpus_in_affinity_mask-to-cpu-set-util.-ch.patch create mode 100644 SOURCES/0274-test-cpu-set-util-add-simple-test-for-cpus_in_affini.patch create mode 100644 SOURCES/0275-test-cpu-set-util-add-a-smoke-test-for-test_parse_cp.patch create mode 100644 SOURCES/0276-pid1-parse-CPUAffinity-in-incremental-fashion.patch create mode 100644 SOURCES/0277-pid1-don-t-reset-setting-from-proc-cmdline-upon-rest.patch create mode 100644 SOURCES/0278-pid1-when-reloading-configuration-forget-old-setting.patch create mode 100644 SOURCES/0279-test-execute-use-CPUSet-too.patch create mode 100644 SOURCES/0280-shared-cpu-set-util-drop-now-unused-cleanup-function.patch create mode 100644 SOURCES/0281-shared-cpu-set-util-make-transfer-of-cpu_set_t-over-.patch create mode 100644 SOURCES/0282-test-cpu-set-util-add-test-for-dbus-conversions.patch create mode 100644 SOURCES/0283-shared-cpu-set-util-introduce-cpu_set_to_range.patch create mode 100644 SOURCES/0284-systemctl-present-CPUAffinity-mask-as-a-list-of-CPU-.patch create mode 100644 SOURCES/0285-shared-cpu-set-util-only-force-range-printing-one-ti.patch create mode 100644 SOURCES/0286-execute-dump-CPUAffinity-as-a-range-string-instead-o.patch create mode 100644 SOURCES/0287-cpu-set-util-use-d-d-format-in-cpu_set_to_range_stri.patch create mode 100644 SOURCES/0288-core-introduce-NUMAPolicy-and-NUMAMask-options.patch create mode 100644 SOURCES/0289-core-disable-CPUAccounting-by-default.patch create mode 100644 SOURCES/0290-set-kptr_restrict-1.patch create mode 100644 SOURCES/0291-cryptsetup-reduce-the-chance-that-we-will-be-OOM-kil.patch create mode 100644 SOURCES/0292-core-job-fix-breakage-of-ordering-dependencies-by-sy.patch create mode 100644 SOURCES/0293-debug-generator-enable-custom-systemd.debug_shell-tt.patch create mode 100644 SOURCES/0294-test-cpu-set-util-fix-comparison-for-allocation-size.patch create mode 100644 SOURCES/0295-test-cpu-set-util-fix-allocation-size-check-on-i386.patch create mode 100644 SOURCES/0296-catalog-fix-name-of-variable.patch create mode 100644 SOURCES/0297-cryptsetup-add-keyfile-timeout-to-allow-a-keydev-tim.patch create mode 100644 SOURCES/0298-cryptsetup-add-documentation-for-keyfile-timeout.patch create mode 100644 SOURCES/0299-cryptsetup-use-unabbrieviated-variable-names.patch create mode 100644 SOURCES/0300-cryptsetup-don-t-assert-on-variable-which-is-optiona.patch create mode 100644 SOURCES/0301-cryptsetup-generator-guess-whether-the-keyfile-argum.patch create mode 100644 SOURCES/0302-crypt-util-Translate-libcryptsetup-log-level-instead.patch create mode 100644 SOURCES/0303-cryptsetup-add-some-commenting-about-EAGAIN-generati.patch create mode 100644 SOURCES/0304-cryptsetup-downgrade-a-log-message-we-ignore.patch create mode 100644 SOURCES/0305-cryptsetup-rework-how-we-log-about-activation-failur.patch create mode 100644 SOURCES/0306-rules-reintroduce-60-alias-kmsg.rules.patch create mode 100644 SOURCES/0307-sd-bus-make-rqueue-wqueue-sizes-of-type-size_t.patch create mode 100644 SOURCES/0308-sd-bus-reorder-bus-ref-and-bus-message-ref-handling.patch create mode 100644 SOURCES/0309-sd-bus-make-sure-dispatch_rqueue-initializes-return-.patch create mode 100644 SOURCES/0310-sd-bus-drop-two-inappropriate-empty-lines.patch create mode 100644 SOURCES/0311-sd-bus-initialize-mutex-after-we-allocated-the-wqueu.patch create mode 100644 SOURCES/0312-sd-bus-always-go-through-sd_bus_unref-to-free-messag.patch create mode 100644 SOURCES/0313-bus-message-introduce-two-kinds-of-references-to-bus.patch create mode 100644 SOURCES/0314-sd-bus-introduce-API-for-re-enqueuing-incoming-messa.patch create mode 100644 SOURCES/0315-sd-event-add-sd_event_source_disable_unref-helper.patch create mode 100644 SOURCES/0316-polkit-when-authorizing-via-PK-let-s-re-resolve-call.patch create mode 100644 SOURCES/0317-sysctl-let-s-by-default-increase-the-numeric-PID-ran.patch create mode 100644 SOURCES/0318-journal-do-not-trigger-assertion-when-journal_file_c.patch create mode 100644 SOURCES/0319-journal-use-cleanup-attribute-at-one-more-place.patch create mode 100644 SOURCES/0320-sd-bus-use-queue-message-references-for-managing-r-w.patch create mode 100644 SOURCES/0321-pid1-make-sure-to-restore-correct-default-values-for.patch create mode 100644 SOURCES/0322-main-introduce-a-define-HIGH_RLIMIT_MEMLOCK-similar-.patch create mode 100644 SOURCES/0323-seccomp-introduce-seccomp_restrict_suid_sgid-for-blo.patch create mode 100644 SOURCES/0324-test-add-test-case-for-restrict_suid_sgid.patch create mode 100644 SOURCES/0325-core-expose-SUID-SGID-restriction-as-new-unit-settin.patch create mode 100644 SOURCES/0326-analyze-check-for-RestrictSUIDSGID-in-systemd-analyz.patch create mode 100644 SOURCES/0327-man-document-the-new-RestrictSUIDSGID-setting.patch create mode 100644 SOURCES/0328-units-turn-on-RestrictSUIDSGID-in-most-of-our-long-r.patch create mode 100644 SOURCES/0329-core-imply-NNP-and-SUID-SGID-restriction-for-Dynamic.patch create mode 100644 SOURCES/0330-cgroup-introduce-support-for-cgroup-v2-CPUSET-contro.patch create mode 100644 SOURCES/0331-pid1-fix-DefaultTasksMax-initialization.patch create mode 100644 SOURCES/0332-cgroup-make-sure-that-cpuset-is-supported-on-cgroup-.patch create mode 100644 SOURCES/0333-test-introduce-TEST-36-NUMAPOLICY.patch create mode 100644 SOURCES/0334-test-replace-tail-f-with-journal-cursor-which-should.patch create mode 100644 SOURCES/0335-test-support-MPOL_LOCAL-matching-in-unpatched-strace.patch create mode 100644 SOURCES/0336-test-make-sure-the-strace-process-is-indeed-dead.patch create mode 100644 SOURCES/0337-test-skip-the-test-on-systems-without-NUMA-support.patch create mode 100644 SOURCES/0338-test-give-strace-some-time-to-initialize.patch create mode 100644 SOURCES/0339-test-add-a-simple-sanity-check-for-systems-without-N.patch create mode 100644 SOURCES/0340-test-drop-the-missed-exit-1-expression.patch create mode 100644 SOURCES/0341-test-replace-cursor-file-with-a-plain-cursor.patch create mode 100644 SOURCES/0342-cryptsetup-Treat-key-file-errors-as-a-failed-passwor.patch create mode 100644 SOURCES/0343-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch create mode 100644 SOURCES/0344-resolved-Recover-missing-PrivateTmp-yes-and-ProtectS.patch create mode 100644 SOURCES/0345-bus_open-leak-sd_event_source-when-udevadm-trigger.patch create mode 100644 SOURCES/0346-core-rework-StopWhenUnneeded-logic.patch create mode 100644 SOURCES/0347-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch create mode 100644 SOURCES/0348-core-fix-re-realization-of-cgroup-siblings.patch create mode 100644 SOURCES/0349-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch create mode 100644 SOURCES/0350-core-transition-to-FINAL_SIGTERM-state-after-ExecSto.patch create mode 100644 SOURCES/0351-sd-journal-close-journal-files-that-were-deleted-by-.patch create mode 100644 SOURCES/0352-sd-journal-remove-the-dead-code-and-actually-fix-146.patch create mode 100644 SOURCES/0353-udev-downgrade-message-when-we-fail-to-set-inotify-w.patch create mode 100644 SOURCES/0354-logind-check-PolicyKit-before-allowing-VT-switch.patch create mode 100644 SOURCES/0355-test-do-not-use-global-variable-to-pass-error.patch create mode 100644 SOURCES/0356-test-install-libraries-required-by-tests.patch create mode 100644 SOURCES/0357-test-introduce-install_zoneinfo.patch create mode 100644 SOURCES/0358-test-replace-duplicated-Makefile-by-symbolic-link.patch create mode 100644 SOURCES/0359-test-add-paths-of-keymaps-in-install_keymaps.patch create mode 100644 SOURCES/0360-test-make-install_keymaps-optionally-install-more-ke.patch create mode 100644 SOURCES/0361-test-fs-util-skip-some-tests-when-running-in-unprivi.patch create mode 100644 SOURCES/0362-test-process-util-skip-several-verifications-when-ru.patch create mode 100644 SOURCES/0363-test-execute-also-check-python3-is-installed-or-not.patch create mode 100644 SOURCES/0364-test-execute-skip-several-tests-when-running-in-cont.patch create mode 100644 SOURCES/0365-test-introduce-test_is_running_from_builddir.patch create mode 100644 SOURCES/0366-test-make-test-catalog-relocatable.patch create mode 100644 SOURCES/0367-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch create mode 100644 SOURCES/0368-test-try-to-determine-QEMU_SMP-dynamically.patch create mode 100644 SOURCES/0369-test-store-coredumps-in-journal.patch create mode 100644 SOURCES/0370-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch create mode 100644 SOURCES/0371-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch create mode 100644 SOURCES/0372-man-be-clearer-that-.timer-time-expressions-need-to-.patch create mode 100644 SOURCES/0373-Add-support-for-opening-files-for-appending.patch create mode 100644 SOURCES/0374-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch create mode 100644 SOURCES/0375-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch create mode 100644 SOURCES/0376-core-move-unit_status_emit_starting_stopping_reloadi.patch create mode 100644 SOURCES/0377-job-when-a-job-was-skipped-due-to-a-failed-condition.patch create mode 100644 SOURCES/0378-core-split-out-all-logic-that-updates-a-Job-on-a-uni.patch create mode 100644 SOURCES/0379-core-make-log-messages-about-units-entering-a-failed.patch create mode 100644 SOURCES/0380-core-log-a-recognizable-message-when-a-unit-succeeds.patch create mode 100644 SOURCES/0381-tests-always-use-the-right-vtable-wrapper-calls.patch create mode 100644 SOURCES/0382-test-execute-allow-filtering-test-cases-by-pattern.patch create mode 100644 SOURCES/0383-test-execute-provide-custom-failure-message.patch create mode 100644 SOURCES/0384-core-ExecCondition-for-services.patch create mode 100644 SOURCES/0385-Drop-support-for-lz4-1.3.0.patch create mode 100644 SOURCES/0386-test-compress-add-test-for-short-decompress_startswi.patch create mode 100644 SOURCES/0387-journal-adapt-for-new-improved-LZ4_decompress_safe_p.patch create mode 100644 SOURCES/0388-fuzz-compress-add-fuzzer-for-compression-and-decompr.patch create mode 100644 SOURCES/0389-seccomp-fix-__NR__sysctl-usage.patch create mode 100644 SOURCES/0390-tmpfiles-fix-crash-with-NULL-in-arg_root-and-other-f.patch create mode 100644 SOURCES/0391-sulogin-shell-Use-force-if-SYSTEMD_SULOGIN_FORCE-set.patch create mode 100644 SOURCES/0392-resolvconf-fixes-for-the-compatibility-interface.patch create mode 100644 SOURCES/0393-mount-don-t-add-Requires-for-tmp.mount.patch create mode 100644 SOURCES/0394-core-coldplug-possible-nop_job.patch create mode 100644 SOURCES/0395-core-add-IODeviceLatencyTargetSec.patch create mode 100644 SOURCES/0396-time-util-Introduce-parse_sec_def_infinity.patch create mode 100644 SOURCES/0397-cgroup-use-structured-initialization.patch create mode 100644 SOURCES/0398-core-add-CPUQuotaPeriodSec.patch create mode 100644 SOURCES/0399-core-downgrade-CPUQuotaPeriodSec-clamping-logs-to-de.patch create mode 100644 SOURCES/0400-sd-bus-avoid-magic-number-in-SASL-length-calculation.patch create mode 100644 SOURCES/0401-sd-bus-fix-SASL-reply-to-empty-AUTH.patch create mode 100644 SOURCES/0402-sd-bus-skip-sending-formatted-UIDs-via-SASL.patch create mode 100644 SOURCES/0403-core-add-MemoryMin.patch create mode 100644 SOURCES/0404-core-introduce-cgroup_add_device_allow.patch create mode 100644 SOURCES/0405-test-remove-support-for-suffix-in-get_testdata_dir.patch create mode 100644 SOURCES/0406-cgroup-Implement-default-propagation-of-MemoryLow-wi.patch create mode 100644 SOURCES/0407-cgroup-Create-UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP.patch create mode 100644 SOURCES/0408-unit-Add-DefaultMemoryMin.patch create mode 100644 SOURCES/0409-cgroup-Polish-hierarchically-aware-protection-docs-a.patch create mode 100644 SOURCES/0410-cgroup-Readd-some-plumbing-for-DefaultMemoryMin.patch create mode 100644 SOURCES/0411-cgroup-Support-0-value-for-memory-protection-directi.patch create mode 100644 SOURCES/0412-cgroup-Test-that-it-s-possible-to-set-memory-protect.patch create mode 100644 SOURCES/0413-cgroup-Check-ancestor-memory-min-for-unified-memory-.patch create mode 100644 SOURCES/0414-cgroup-Respect-DefaultMemoryMin-when-setting-memory..patch create mode 100644 SOURCES/0415-cgroup-Mark-memory-protections-as-explicitly-set-in-.patch create mode 100644 SOURCES/0416-meson-allow-setting-the-version-string-during-config.patch create mode 100644 SOURCES/0417-core-don-t-consider-SERVICE_SKIP_CONDITION-for-abnor.patch create mode 100644 SOURCES/0418-selinux-do-preprocessor-check-only-in-selinux-access.patch create mode 100644 SOURCES/0419-basic-cgroup-util-introduce-cg_get_keyed_attribute_f.patch create mode 100644 SOURCES/0420-shared-add-generic-logic-for-waiting-for-a-unit-to-e.patch create mode 100644 SOURCES/0421-shared-fix-assert-call.patch create mode 100644 SOURCES/0422-shared-Don-t-try-calling-NULL-callback-in-bus_wait_f.patch create mode 100644 SOURCES/0423-shared-add-NULL-callback-check-in-one-more-place.patch create mode 100644 SOURCES/0424-core-introduce-support-for-cgroup-freezer.patch create mode 100644 SOURCES/0425-core-cgroup-fix-return-value-of-unit_cgorup_freezer_.patch create mode 100644 SOURCES/0426-core-fix-the-return-value-in-order-to-make-sure-we-d.patch create mode 100644 SOURCES/0427-test-add-test-for-cgroup-v2-freezer-support.patch create mode 100644 SOURCES/0428-fix-mis-merge.patch create mode 100644 SOURCES/0429-tests-sleep-a-bit-and-give-kernel-time-to-perform-th.patch create mode 100644 SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch create mode 100644 SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch create mode 100644 SOURCES/0432-units-add-generic-boot-complete.target.patch create mode 100644 SOURCES/0433-man-document-new-boot-complete.target-unit.patch create mode 100644 SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch create mode 100644 SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch create mode 100644 SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch create mode 100644 SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch create mode 100644 SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch create mode 100644 SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch create mode 100644 SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch create mode 100644 SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch create mode 100644 SOURCES/0442-units-drop-reference-to-sushell-man-page.patch create mode 100644 SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch create mode 100644 SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch create mode 100644 SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch create mode 100644 SOURCES/0446-systemctl-reindent-table.patch create mode 100644 SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch create mode 100644 SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch create mode 100644 SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch create mode 100644 SOURCES/0450-systemctl-add-new-show-transaction-switch.patch create mode 100644 SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch create mode 100644 SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch create mode 100644 SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch create mode 100644 SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch create mode 100644 SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch create mode 100644 SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch create mode 100644 SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch create mode 100644 SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch create mode 100644 SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch create mode 100644 SOURCES/0460-basic-parse-util-add-safe_atoux64.patch create mode 100644 SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch create mode 100644 SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch create mode 100644 SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch create mode 100644 SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch create mode 100644 SOURCES/0465-user-util-be-stricter-in-parse_uid.patch create mode 100644 SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch create mode 100644 SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch create mode 100644 SOURCES/0468-tests-beef-up-integer-parsing-tests.patch create mode 100644 SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch create mode 100644 SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch create mode 100644 SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch create mode 100644 SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch create mode 100644 SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch create mode 100644 SOURCES/0474-user-util-rework-how-we-validate-user-names.patch create mode 100644 SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch create mode 100644 SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch create mode 100644 SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch create mode 100644 SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch create mode 100644 SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch create mode 100644 SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch create mode 100644 SOURCES/0481-fix-typo-in-ProtectSystem-option.patch create mode 100644 SOURCES/0482-remove-references-of-non-existent-man-pages.patch create mode 100644 SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch create mode 100644 SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch create mode 100644 SOURCES/0485-test-add-test-case-for-locale_is_installed.patch create mode 100644 SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch create mode 100644 SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch create mode 100644 SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch create mode 100644 SOURCES/0489-test-fix-a-memleak.patch create mode 100644 SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch create mode 100644 SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch create mode 100644 SOURCES/0492-introduce-setsockopt_int-helper.patch create mode 100644 SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch create mode 100644 SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch create mode 100644 SOURCES/0495-resolved-tweak-cmsg-calculation.patch create mode 100644 SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch create mode 100644 SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch create mode 100644 SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch create mode 100644 SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch create mode 100644 SOURCES/0500-use-link-to-RHEL-8-docs.patch create mode 100644 SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch create mode 100644 SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch create mode 100644 SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch create mode 100644 SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch create mode 100644 SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch create mode 100644 SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch create mode 100644 SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch create mode 100644 SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch create mode 100644 SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch create mode 100644 SOURCES/0510-test-missing-die.patch create mode 100644 SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch create mode 100644 SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch create mode 100644 SOURCES/0513-udev-test-fix-test-skip-condition.patch create mode 100644 SOURCES/0514-udev-test-fix-missing-directory-test-run.patch create mode 100644 SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch create mode 100644 SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch create mode 100644 SOURCES/0517-test-udev-use-proper-semantics-for-too-long-line-wit.patch create mode 100644 SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch create mode 100644 SOURCES/0519-test-udev-add-more-tests-for-line-continuation.patch create mode 100644 SOURCES/0520-test-udev-fix-alignment-and-drop-unnecessary-white-s.patch create mode 100644 SOURCES/0521-test-udev-test.pl-cleanup-if-skipping-test.patch create mode 100644 SOURCES/0522-test-add-test-cases-for-empty-string-match.patch create mode 100644 SOURCES/0523-test-add-test-case-for-multi-matches-when-use.patch create mode 100644 SOURCES/0524-udev-test-do-not-rely-on-mail-group-being-defined.patch create mode 100644 SOURCES/0525-test-udev-test.pl-allow-multiple-devices-per-test.patch create mode 100644 SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch create mode 100644 SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch create mode 100644 SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch create mode 100644 SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch create mode 100644 SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch create mode 100644 SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch create mode 100644 SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch create mode 100644 SOURCES/0533-test-udev-test.pl-Make-some-tests-a-little-harder.patch create mode 100644 SOURCES/0534-test-udev-test.pl-remove-bogus-rules-from-magic-subs.patch create mode 100644 SOURCES/0535-test-udev-test.pl-merge-space-and-var-with-space-tes.patch create mode 100644 SOURCES/0536-test-udev-test.pl-merge-import-parent-tests-into-one.patch create mode 100644 SOURCES/0537-test-udev-test.pl-count-good-results.patch create mode 100644 SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch create mode 100644 SOURCES/0539-test-udev-test.pl-add-repeat-count.patch create mode 100644 SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch create mode 100644 SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch create mode 100644 SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch create mode 100644 SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch create mode 100644 SOURCES/0544-Revert-test-add-test-cases-for-empty-string-match-an.patch create mode 100644 SOURCES/0545-test-sys-script.py-add-missing-DEVNAME-entries-to-ue.patch create mode 100644 SOURCES/0546-sd-event-split-out-helper-functions-for-reshuffling-.patch create mode 100644 SOURCES/0547-sd-event-split-out-enable-and-disable-codepaths-from.patch create mode 100644 SOURCES/0548-sd-event-mention-that-two-debug-logged-events-are-ig.patch create mode 100644 SOURCES/0549-sd-event-split-clock-data-allocation-out-of-sd_event.patch create mode 100644 SOURCES/0550-sd-event-split-out-code-to-add-remove-timer-event-so.patch create mode 100644 SOURCES/0551-sd-event-fix-delays-assert-brain-o-17790.patch create mode 100644 SOURCES/0552-sd-event-let-s-suffix-last_run-last_log-with-_usec.patch create mode 100644 SOURCES/0553-sd-event-refuse-running-default-event-loops-in-any-o.patch create mode 100644 SOURCES/0554-sd-event-ref-event-loop-while-in-sd_event_prepare-ot.patch create mode 100644 SOURCES/0555-sd-event-follow-coding-style-with-naming-return-para.patch create mode 100644 SOURCES/0556-sd-event-remove-earliest_index-latest_index-into-com.patch create mode 100644 SOURCES/0557-sd-event-update-state-at-the-end-in-event_source_ena.patch create mode 100644 SOURCES/0558-sd-event-increase-n_enabled_child_sources-just-once.patch create mode 100644 SOURCES/0559-sd-event-add-ability-to-ratelimit-event-sources.patch create mode 100644 SOURCES/0560-test-add-ratelimiting-test.patch create mode 100644 SOURCES/0561-core-prevent-excessive-proc-self-mountinfo-parsing.patch create mode 100644 SOURCES/0562-udev-run-link_update-with-increased-retry-count-in-s.patch create mode 100644 SOURCES/0563-pam-systemd-use-secure_getenv-rather-than-getenv.patch create mode 100755 SOURCES/20-grubby.install create mode 100644 SOURCES/20-yama-ptrace.conf create mode 100644 SOURCES/inittab create mode 100755 SOURCES/purge-nobody-user create mode 100644 SOURCES/rc.local create mode 100644 SOURCES/split-files.py create mode 100644 SOURCES/sysctl.conf.README create mode 100644 SOURCES/systemd-journal-gatewayd.xml create mode 100644 SOURCES/systemd-journal-remote.xml create mode 100644 SOURCES/systemd-udev-trigger-no-reload.conf create mode 100644 SOURCES/systemd-user create mode 100644 SOURCES/triggers.systemd create mode 100644 SOURCES/yum-protect-systemd.conf create mode 100644 SPECS/systemd.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..493c917 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/systemd-239.tar.gz diff --git a/.systemd.metadata b/.systemd.metadata new file mode 100644 index 0000000..a883c33 --- /dev/null +++ b/.systemd.metadata @@ -0,0 +1 @@ +8803baa484cbe36680463c8c5e6febeff074b8e7 SOURCES/systemd-239.tar.gz diff --git a/SOURCES/0001-build-sys-Detect-whether-struct-statx-is-defined-in-.patch b/SOURCES/0001-build-sys-Detect-whether-struct-statx-is-defined-in-.patch new file mode 100644 index 0000000..aadd1d6 --- /dev/null +++ b/SOURCES/0001-build-sys-Detect-whether-struct-statx-is-defined-in-.patch @@ -0,0 +1,105 @@ +From 79df4db3fd122f5040bdf2225c3047375de3b0d2 Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Sun, 15 Jul 2018 22:43:35 -0700 +Subject: [PATCH] build-sys: Detect whether struct statx is defined in + sys/stat.h +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Starting with glibc 2.27.9000-36.fc29, include file sys/stat.h will have a +definition for struct statx, in which case include file linux/stat.h should be +avoided, in order to prevent a duplicate definition. + + In file included from ../src/basic/missing.h:18, + from ../src/basic/util.h:28, + from ../src/basic/hashmap.h:10, + from ../src/shared/bus-util.h:12, + from ../src/libsystemd/sd-bus/bus-creds.c:11: + /usr/include/linux/stat.h:99:8: error: redefinition of ‘struct statx’ + struct statx { + ^~~~~ + In file included from /usr/include/sys/stat.h:446, + from ../src/basic/util.h:19, + from ../src/basic/hashmap.h:10, + from ../src/shared/bus-util.h:12, + from ../src/libsystemd/sd-bus/bus-creds.c:11: + /usr/include/bits/statx.h:36:8: note: originally defined here + struct statx + ^~~~~ + +Extend our meson.build to look for struct statx when only sys/stat.h is +included and, in that case, do not include linux/stat.h anymore. + +Tested that systemd builds correctly when using a glibc version that includes a +definition for struct statx. + +glibc Fedora RPM update: +https://src.fedoraproject.org/rpms/glibc/c/28cb5d31fc1e5887912283c889689c47076278ae + +glibc upstream commit: +https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=fd70af45528d59a00eb3190ef6706cb299488fcd +--- + meson.build | 5 +++++ + src/basic/missing.h | 5 ++++- + src/basic/xattr-util.c | 1 - + 3 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/meson.build b/meson.build +index 04331dd41a..a0e7240708 100644 +--- a/meson.build ++++ b/meson.build +@@ -425,6 +425,7 @@ decl_headers = ''' + #include + ''' + # FIXME: key_serial_t is only defined in keyutils.h, this is bound to fail ++# FIXME: these should use -D_GNU_SOURCE, since that is defined at build time + + foreach decl : ['char16_t', + 'char32_t', +@@ -439,6 +440,10 @@ foreach decl : ['char16_t', + conf.set10('HAVE_' + decl.underscorify().to_upper(), have) + endforeach + ++conf.set10('HAVE_STRUCT_STATX_IN_SYS_STAT_H', cc.sizeof('struct statx', prefix : ''' ++#include ++''', args : '-D_GNU_SOURCE') > 0) ++ + foreach decl : [['IFLA_INET6_ADDR_GEN_MODE', 'linux/if_link.h'], + ['IN6_ADDR_GEN_MODE_STABLE_PRIVACY', 'linux/if_link.h'], + ['IFLA_VRF_TABLE', 'linux/if_link.h'], +diff --git a/src/basic/missing.h b/src/basic/missing.h +index 71a07d0574..14ad3d4914 100644 +--- a/src/basic/missing.h ++++ b/src/basic/missing.h +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -25,6 +24,10 @@ + #include + #include + ++#if !HAVE_STRUCT_STATX_IN_SYS_STAT_H ++#include ++#endif ++ + #if HAVE_AUDIT + #include + #endif +diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c +index c5c55ea846..0ee0979837 100644 +--- a/src/basic/xattr-util.c ++++ b/src/basic/xattr-util.c +@@ -2,7 +2,6 @@ + + #include + #include +-#include + #include + #include + #include diff --git a/SOURCES/0002-logind-set-RemoveIPC-to-false-by-default.patch b/SOURCES/0002-logind-set-RemoveIPC-to-false-by-default.patch new file mode 100644 index 0000000..d5244cd --- /dev/null +++ b/SOURCES/0002-logind-set-RemoveIPC-to-false-by-default.patch @@ -0,0 +1,50 @@ +From 0b3833d6c3b751c6dfb40eeb2ef852984c58f546 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 1 Aug 2018 10:58:28 +0200 +Subject: [PATCH] logind: set RemoveIPC to false by default + +Resolves: #1523233 +--- + man/logind.conf.xml | 2 +- + src/login/logind-core.c | 2 +- + src/login/logind.conf.in | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/man/logind.conf.xml b/man/logind.conf.xml +index 9e88764c6f..7d7e869a26 100644 +--- a/man/logind.conf.xml ++++ b/man/logind.conf.xml +@@ -319,7 +319,7 @@ + user fully logs out. Takes a boolean argument. If enabled, the user may not consume IPC resources after the + last of the user's sessions terminated. This covers System V semaphores, shared memory and message queues, as + well as POSIX shared memory and message queues. Note that IPC objects of the root user and other system users +- are excluded from the effect of this setting. Defaults to yes. ++ are excluded from the effect of this setting. Defaults to no. + + + +diff --git a/src/login/logind-core.c b/src/login/logind-core.c +index dbae4bf5af..511e3acf8f 100644 +--- a/src/login/logind-core.c ++++ b/src/login/logind-core.c +@@ -25,7 +25,7 @@ void manager_reset_config(Manager *m) { + + m->n_autovts = 6; + m->reserve_vt = 6; +- m->remove_ipc = true; ++ m->remove_ipc = false; + m->inhibit_delay_max = 5 * USEC_PER_SEC; + m->handle_power_key = HANDLE_POWEROFF; + m->handle_suspend_key = HANDLE_SUSPEND; +diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in +index 1029e29bc7..c7346f9819 100644 +--- a/src/login/logind.conf.in ++++ b/src/login/logind.conf.in +@@ -32,6 +32,6 @@ + #IdleAction=ignore + #IdleActionSec=30min + #RuntimeDirectorySize=10% +-#RemoveIPC=yes ++#RemoveIPC=no + #InhibitorsMax=8192 + #SessionsMax=8192 diff --git a/SOURCES/0003-pid1-bump-DefaultTasksMax-to-80-of-the-kernel-pid.ma.patch b/SOURCES/0003-pid1-bump-DefaultTasksMax-to-80-of-the-kernel-pid.ma.patch new file mode 100644 index 0000000..baf095b --- /dev/null +++ b/SOURCES/0003-pid1-bump-DefaultTasksMax-to-80-of-the-kernel-pid.ma.patch @@ -0,0 +1,53 @@ +From b924c79720cc2bf2edf75fa3ff43bb4954fccf1f Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 1 Aug 2018 13:19:39 +0200 +Subject: [PATCH] pid1: bump DefaultTasksMax to 80% of the kernel pid.max value + +This should be hopefully high enough even for the very big deployments. + +Resolves: #1523236 +--- + man/systemd-system.conf.xml | 2 +- + src/basic/cgroup-util.h | 2 +- + src/core/system.conf.in | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml +index a914ef2523..085086200a 100644 +--- a/man/systemd-system.conf.xml ++++ b/man/systemd-system.conf.xml +@@ -339,7 +339,7 @@ + Configure the default value for the per-unit TasksMax= setting. See + systemd.resource-control5 + for details. This setting applies to all unit types that support resource control settings, with the exception +- of slice units. Defaults to 15%, which equals 4915 with the kernel's defaults on the host, but might be smaller ++ of slice units. Defaults to 80%, which equals 26214 with the kernel's defaults on the host, but might be smaller + in OS containers. + + +diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h +index 1a28a8163a..f10c26ad51 100644 +--- a/src/basic/cgroup-util.h ++++ b/src/basic/cgroup-util.h +@@ -100,7 +100,7 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { + } + + /* Default resource limits */ +-#define DEFAULT_TASKS_MAX_PERCENTAGE 15U /* 15% of PIDs, 4915 on default settings */ ++#define DEFAULT_TASKS_MAX_PERCENTAGE 80U /* 80% of PIDs, 26214 on default settings */ + #define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */ + + typedef enum CGroupUnified { +diff --git a/src/core/system.conf.in b/src/core/system.conf.in +index f0a59a79a5..653ec6b8c9 100644 +--- a/src/core/system.conf.in ++++ b/src/core/system.conf.in +@@ -45,7 +45,7 @@ + #DefaultBlockIOAccounting=no + #DefaultMemoryAccounting=@MEMORY_ACCOUNTING_DEFAULT@ + #DefaultTasksAccounting=yes +-#DefaultTasksMax=15% ++#DefaultTasksMax=80% + #DefaultLimitCPU= + #DefaultLimitFSIZE= + #DefaultLimitDATA= diff --git a/SOURCES/0004-Avoid-tmp-being-mounted-as-tmpfs-without-the-user-s-.patch b/SOURCES/0004-Avoid-tmp-being-mounted-as-tmpfs-without-the-user-s-.patch new file mode 100644 index 0000000..b2de3d8 --- /dev/null +++ b/SOURCES/0004-Avoid-tmp-being-mounted-as-tmpfs-without-the-user-s-.patch @@ -0,0 +1,51 @@ +From f58c5ced373c2532b5cc44ba2e0c3a28b41472f2 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 15 May 2018 09:24:20 +0200 +Subject: [PATCH] Avoid /tmp being mounted as tmpfs without the user's will + +Ensure PrivateTmp doesn't require tmpfs through tmp.mount, but rather +adds an After relationship. + +rhel-only + +Resolves: #1578772 +--- + src/core/unit.c | 12 ++++++------ + units/basic.target | 3 ++- + 2 files changed, 8 insertions(+), 7 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 113205bf25..c9f756c9c7 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -982,13 +982,13 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { + return 0; + + if (c->private_tmp) { +- const char *p; ++ r = unit_add_dependency_by_name(u, UNIT_AFTER, "tmp.mount", NULL, true, UNIT_DEPENDENCY_FILE); ++ if (r < 0) ++ return r; + +- FOREACH_STRING(p, "/tmp", "/var/tmp") { +- r = unit_require_mounts_for(u, p, UNIT_DEPENDENCY_FILE); +- if (r < 0) +- return r; +- } ++ r = unit_require_mounts_for(u, "/var/tmp", UNIT_DEPENDENCY_FILE); ++ if (r < 0) ++ return r; + + r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, NULL, true, UNIT_DEPENDENCY_FILE); + if (r < 0) +diff --git a/units/basic.target b/units/basic.target +index 4f44292249..8fc7c73ef2 100644 +--- a/units/basic.target ++++ b/units/basic.target +@@ -19,4 +19,5 @@ After=sysinit.target sockets.target paths.target slices.target tmp.mount + # require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as + # we support that unit being masked, and this should not be considered an error. + RequiresMountsFor=/var /var/tmp +-Wants=tmp.mount ++# RHEL-only: Disable /tmp on tmpfs. ++#Wants=tmp.mount diff --git a/SOURCES/0005-pid1-bump-maximum-number-of-process-in-user-slice-to.patch b/SOURCES/0005-pid1-bump-maximum-number-of-process-in-user-slice-to.patch new file mode 100644 index 0000000..2beec8c --- /dev/null +++ b/SOURCES/0005-pid1-bump-maximum-number-of-process-in-user-slice-to.patch @@ -0,0 +1,35 @@ +From c7f77dfd2bfa593bfbbdf82eea8b600ca1b46f4c Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 1 Aug 2018 17:17:07 +0200 +Subject: [PATCH] pid1: bump maximum number of process in user slice to 80% of + pid_max + +Related: #1523236 +--- + src/basic/cgroup-util.h | 2 +- + units/user-.slice.d/10-defaults.conf | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h +index f10c26ad51..26e3ae0404 100644 +--- a/src/basic/cgroup-util.h ++++ b/src/basic/cgroup-util.h +@@ -101,7 +101,7 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { + + /* Default resource limits */ + #define DEFAULT_TASKS_MAX_PERCENTAGE 80U /* 80% of PIDs, 26214 on default settings */ +-#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */ ++#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 80U /* 80% of PIDs, 26214 on default settings */ + + typedef enum CGroupUnified { + CGROUP_UNIFIED_UNKNOWN = -1, +diff --git a/units/user-.slice.d/10-defaults.conf b/units/user-.slice.d/10-defaults.conf +index 95ab11b30b..efc9d37c8e 100644 +--- a/units/user-.slice.d/10-defaults.conf ++++ b/units/user-.slice.d/10-defaults.conf +@@ -12,4 +12,4 @@ Description=User Slice of UID %j + After=systemd-user-sessions.service + + [Slice] +-TasksMax=33% ++TasksMax=80% diff --git a/SOURCES/0006-rules-automatically-online-hot-plugged-CPUs.patch b/SOURCES/0006-rules-automatically-online-hot-plugged-CPUs.patch new file mode 100644 index 0000000..78a2656 --- /dev/null +++ b/SOURCES/0006-rules-automatically-online-hot-plugged-CPUs.patch @@ -0,0 +1,33 @@ +From 787420ac2ba9c404e13db08601946bde263523f8 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 22 Sep 2014 07:41:06 +0200 +Subject: [PATCH] rules: automatically online hot-plugged CPUs + +Related: #1523227 +--- + rules/40-redhat.rules | 3 +++ + rules/meson.build | 1 + + 2 files changed, 4 insertions(+) + create mode 100644 rules/40-redhat.rules + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +new file mode 100644 +index 0000000000..2b494e57cf +--- /dev/null ++++ b/rules/40-redhat.rules +@@ -0,0 +1,3 @@ ++# do not edit this file, it will be overwritten on update ++ ++SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online}="1" +diff --git a/rules/meson.build b/rules/meson.build +index b6a32ba77e..e7e4362c0c 100644 +--- a/rules/meson.build ++++ b/rules/meson.build +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: LGPL-2.1+ + + rules = files(''' ++ 40-redhat.rules + 60-block.rules + 60-cdrom_id.rules + 60-drm.rules diff --git a/SOURCES/0007-rules-add-rule-for-naming-Dell-iDRAC-USB-Virtual-NIC.patch b/SOURCES/0007-rules-add-rule-for-naming-Dell-iDRAC-USB-Virtual-NIC.patch new file mode 100644 index 0000000..8682ffc --- /dev/null +++ b/SOURCES/0007-rules-add-rule-for-naming-Dell-iDRAC-USB-Virtual-NIC.patch @@ -0,0 +1,37 @@ +From 2991b22f5f40a66ad1cc088e502e7f40ae1806c2 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 22 Sep 2014 07:53:52 +0200 +Subject: [PATCH] rules: add rule for naming Dell iDRAC USB Virtual NIC as + 'idrac' + +Related: #1523227 +--- + rules/73-idrac.rules | 6 ++++++ + rules/meson.build | 1 + + 2 files changed, 7 insertions(+) + create mode 100644 rules/73-idrac.rules + +diff --git a/rules/73-idrac.rules b/rules/73-idrac.rules +new file mode 100644 +index 0000000000..d67fc425b1 +--- /dev/null ++++ b/rules/73-idrac.rules +@@ -0,0 +1,6 @@ ++# do not edit this file, it will be overwritten on update ++ ++# On Dell PowerEdge systems, the iDRAC7 and later support a USB Virtual NIC ++# with terminates in the iDRAC. Help identify this with 'idrac' ++ ++ACTION=="add", SUBSYSTEM=="net", SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", ATTRS{idProduct}=="a102", NAME="idrac" +diff --git a/rules/meson.build b/rules/meson.build +index e7e4362c0c..e04a18aca6 100644 +--- a/rules/meson.build ++++ b/rules/meson.build +@@ -17,6 +17,7 @@ rules = files(''' + 70-joystick.rules + 70-mouse.rules + 70-touchpad.rules ++ 73-idrac.rules + 75-net-description.rules + 75-probe_mtd.rules + 78-sound-card.rules diff --git a/SOURCES/0008-rules-enable-memory-hotplug.patch b/SOURCES/0008-rules-enable-memory-hotplug.patch new file mode 100644 index 0000000..c724b28 --- /dev/null +++ b/SOURCES/0008-rules-enable-memory-hotplug.patch @@ -0,0 +1,22 @@ +From d5215083fa1d10f1624ab2f0fb5ba420a2594938 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Wed, 13 May 2015 16:56:44 +0200 +Subject: [PATCH] rules: enable memory hotplug + +Related: #1523227 +--- + rules/40-redhat.rules | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 2b494e57cf..8231caae98 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -1,3 +1,7 @@ + # do not edit this file, it will be overwritten on update + ++# CPU hotadd request + SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online}="1" ++ ++# Memory hotadd request ++SUBSYSTEM=="memory", ACTION=="add", ATTR{state}=="offline", ATTR{state}="online" diff --git a/SOURCES/0009-rules-reload-sysctl-settings-when-the-bridge-module-.patch b/SOURCES/0009-rules-reload-sysctl-settings-when-the-bridge-module-.patch new file mode 100644 index 0000000..24fa821 --- /dev/null +++ b/SOURCES/0009-rules-reload-sysctl-settings-when-the-bridge-module-.patch @@ -0,0 +1,22 @@ +From 4a7602e27a50828ac8a0eb6b83a1c2c722af652d Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Wed, 13 May 2015 17:11:48 +0200 +Subject: [PATCH] rules: reload sysctl settings when the bridge module is + loaded + +Related: #1523227 +--- + rules/40-redhat.rules | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 8231caae98..556a3a3a90 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -5,3 +5,6 @@ SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online} + + # Memory hotadd request + SUBSYSTEM=="memory", ACTION=="add", ATTR{state}=="offline", ATTR{state}="online" ++ ++# reload sysctl.conf / sysctl.conf.d settings when the bridge module is loaded ++ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/proc/sys/net/bridge" diff --git a/SOURCES/0010-rules-load-sg-module.patch b/SOURCES/0010-rules-load-sg-module.patch new file mode 100644 index 0000000..92f88b2 --- /dev/null +++ b/SOURCES/0010-rules-load-sg-module.patch @@ -0,0 +1,21 @@ +From a42b57dc8b265f183a8fb6fe9ae32a9d77cbb7c5 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Wed, 20 May 2015 12:34:18 +0200 +Subject: [PATCH] rules: load sg module + +Related: #1523227 +--- + rules/40-redhat.rules | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 556a3a3a90..305e752285 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -8,3 +8,6 @@ SUBSYSTEM=="memory", ACTION=="add", ATTR{state}=="offline", ATTR{state}="online" + + # reload sysctl.conf / sysctl.conf.d settings when the bridge module is loaded + ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/proc/sys/net/bridge" ++ ++# load SCSI generic (sg) driver ++SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST!="[module/sg]", RUN+="/sbin/modprobe -bv sg" diff --git a/SOURCES/0011-rules-prandom-character-device-node-permissions.patch b/SOURCES/0011-rules-prandom-character-device-node-permissions.patch new file mode 100644 index 0000000..1ccc71b --- /dev/null +++ b/SOURCES/0011-rules-prandom-character-device-node-permissions.patch @@ -0,0 +1,21 @@ +From 21c96c3781f473cdbfe7acdb1affba75b50081f1 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Tue, 22 Sep 2015 12:28:28 +0200 +Subject: [PATCH] rules: prandom character device node permissions + +Related: #1523227 +--- + rules/40-redhat.rules | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 305e752285..9a48adde19 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -11,3 +11,6 @@ ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/sys + + # load SCSI generic (sg) driver + SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST!="[module/sg]", RUN+="/sbin/modprobe -bv sg" ++ ++# Rule for prandom character device node permissions ++KERNEL=="prandom", MODE="0644" diff --git a/SOURCES/0012-rules-load-sg-driver-also-when-scsi_target-appears-4.patch b/SOURCES/0012-rules-load-sg-driver-also-when-scsi_target-appears-4.patch new file mode 100644 index 0000000..67627cc --- /dev/null +++ b/SOURCES/0012-rules-load-sg-driver-also-when-scsi_target-appears-4.patch @@ -0,0 +1,22 @@ +From fab2dff96f59e0851884b4ef32dccab763f5eef1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Nykr=C3=BDn?= +Date: Thu, 18 Aug 2016 14:51:19 +0200 +Subject: [PATCH] rules: load sg driver also when scsi_target appears (#45) + +Related: #1523227 +--- + rules/40-redhat.rules | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 9a48adde19..3335fe5075 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -11,6 +11,7 @@ ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/sys + + # load SCSI generic (sg) driver + SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST!="[module/sg]", RUN+="/sbin/modprobe -bv sg" ++SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_target", TEST!="[module/sg]", RUN+="/sbin/modprobe -bv sg" + + # Rule for prandom character device node permissions + KERNEL=="prandom", MODE="0644" diff --git a/SOURCES/0013-rules-don-t-hoplug-memory-on-s390x.patch b/SOURCES/0013-rules-don-t-hoplug-memory-on-s390x.patch new file mode 100644 index 0000000..2c813f1 --- /dev/null +++ b/SOURCES/0013-rules-don-t-hoplug-memory-on-s390x.patch @@ -0,0 +1,23 @@ +From fd091394e52cd652ff5163735b2a91a8c0efe415 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Tue, 13 Sep 2016 13:18:38 +0200 +Subject: [PATCH] rules: don't hoplug memory on s390x + +Related: #1523227 +--- + rules/40-redhat.rules | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 3335fe5075..4c56950dab 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -4,7 +4,7 @@ + SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online}="1" + + # Memory hotadd request +-SUBSYSTEM=="memory", ACTION=="add", ATTR{state}=="offline", ATTR{state}="online" ++SUBSYSTEM=="memory", ACTION=="add", PROGRAM="/usr/bin/systemd-detect-virt", RESULT!="zvm", ATTR{state}=="offline", ATTR{state}="online" + + # reload sysctl.conf / sysctl.conf.d settings when the bridge module is loaded + ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/proc/sys/net/bridge" diff --git a/SOURCES/0014-rules-disable-auto-online-of-hot-plugged-memory-on-I.patch b/SOURCES/0014-rules-disable-auto-online-of-hot-plugged-memory-on-I.patch new file mode 100644 index 0000000..b4d2386 --- /dev/null +++ b/SOURCES/0014-rules-disable-auto-online-of-hot-plugged-memory-on-I.patch @@ -0,0 +1,24 @@ +From a0802638f02b964cb9d2d68bad009561b2bcc910 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 16 Sep 2016 14:45:01 +0200 +Subject: [PATCH] rules: disable auto-online of hot-plugged memory on IBM z + Systems + +Related: #1523227 +--- + rules/40-redhat.rules | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 4c56950dab..c3df320234 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -4,7 +4,7 @@ + SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online}="1" + + # Memory hotadd request +-SUBSYSTEM=="memory", ACTION=="add", PROGRAM="/usr/bin/systemd-detect-virt", RESULT!="zvm", ATTR{state}=="offline", ATTR{state}="online" ++SUBSYSTEM=="memory", ACTION=="add", PROGRAM=="/bin/uname -p", RESULT!="s390*", ATTR{state}=="offline", ATTR{state}="online" + + # reload sysctl.conf / sysctl.conf.d settings when the bridge module is loaded + ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/proc/sys/net/bridge" diff --git a/SOURCES/0015-rules-introduce-old-style-by-path-symlinks-for-FCP-b.patch b/SOURCES/0015-rules-introduce-old-style-by-path-symlinks-for-FCP-b.patch new file mode 100644 index 0000000..4305361 --- /dev/null +++ b/SOURCES/0015-rules-introduce-old-style-by-path-symlinks-for-FCP-b.patch @@ -0,0 +1,39 @@ +From 0c5b8096cb23701f8048dba33a38e1b55249cab3 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 28 Mar 2018 17:22:30 +0200 +Subject: [PATCH] rules: introduce old-style by-path symlinks for FCP based + SCSI devices + +Related: #1523227 +--- + rules/40-redhat.rules | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index c3df320234..8ac96933c3 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -15,3 +15,23 @@ SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_target", TEST!="[module/sg]", RUN+="/sbin + + # Rule for prandom character device node permissions + KERNEL=="prandom", MODE="0644" ++ ++# Rules for creating the ID_PATH for SCSI devices based on the CCW bus ++# using the form: ccw--zfcp-: ++# ++ACTION=="remove", GOTO="zfcp_scsi_device_end" ++ ++# ++# Set environment variable "ID_ZFCP_BUS" to "1" if the devices ++# (both disk and partition) are SCSI devices based on FCP devices ++# ++KERNEL=="sd*", SUBSYSTEMS=="ccw", DRIVERS=="zfcp", ENV{.ID_ZFCP_BUS}="1" ++ ++# For SCSI disks ++KERNEL=="sd*[!0-9]", SUBSYSTEMS=="scsi", ENV{.ID_ZFCP_BUS}=="1", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-path/ccw-$attr{hba_id}-zfcp-$attr{wwpn}:$attr{fcp_lun}" ++ ++ ++# For partitions on a SCSI disk ++KERNEL=="sd*[0-9]", SUBSYSTEMS=="scsi", ENV{.ID_ZFCP_BUS}=="1", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-path/ccw-$attr{hba_id}-zfcp-$attr{wwpn}:$attr{fcp_lun}-part%n" ++ ++LABEL="zfcp_scsi_device_end" diff --git a/SOURCES/0016-Revert-udev-remove-WAIT_FOR-key.patch b/SOURCES/0016-Revert-udev-remove-WAIT_FOR-key.patch new file mode 100644 index 0000000..b6bb114 --- /dev/null +++ b/SOURCES/0016-Revert-udev-remove-WAIT_FOR-key.patch @@ -0,0 +1,123 @@ +From 1bb734a44952a51285057409ba7b1c3e7a162cea Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 2 Aug 2018 13:16:49 +0200 +Subject: [PATCH] Revert "udev: remove WAIT_FOR key" + +This reverts commit f2b8052fb648b788936dd3e85be6a9aca90fbb2f. + +Resolves: #1523213 +--- + man/udev.xml | 9 +++++++ + src/udev/udev-rules.c | 50 +++++++++++++++++++++++++++++++++++++++ + test/rule-syntax-check.py | 2 +- + 3 files changed, 60 insertions(+), 1 deletion(-) + +diff --git a/man/udev.xml b/man/udev.xml +index 15e6d8eae1..bdf901a8f0 100644 +--- a/man/udev.xml ++++ b/man/udev.xml +@@ -515,6 +515,15 @@ + + + ++ ++ WAIT_FOR ++ ++ Wait for a file to become available or until a timeout of ++ 10 seconds expires. The path is relative to the sysfs device; ++ if no path is specified, this waits for an attribute to appear. ++ ++ ++ + + OPTIONS + +diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c +index f029395884..58af863f3d 100644 +--- a/src/udev/udev-rules.c ++++ b/src/udev/udev-rules.c +@@ -676,6 +676,41 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi + return 0; + } + ++#define WAIT_LOOP_PER_SECOND 50 ++static int wait_for_file(struct udev_device *dev, const char *file, int timeout) { ++ char filepath[UTIL_PATH_SIZE]; ++ char devicepath[UTIL_PATH_SIZE]; ++ struct stat stats; ++ int loop = timeout * WAIT_LOOP_PER_SECOND; ++ ++ /* a relative path is a device attribute */ ++ devicepath[0] = '\0'; ++ if (file[0] != '/') { ++ strscpyl(devicepath, sizeof(devicepath), udev_device_get_syspath(dev), NULL); ++ strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); ++ file = filepath; ++ } ++ ++ while (--loop) { ++ const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND }; ++ ++ /* lookup file */ ++ if (stat(file, &stats) == 0) { ++ log_debug("file '%s' appeared after %i loops", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); ++ return 0; ++ } ++ /* make sure, the device did not disappear in the meantime */ ++ if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { ++ log_debug("device disappeared while waiting for '%s'", file); ++ return -2; ++ } ++ log_debug("wait for '%s' for %i mseconds", file, 1000 / WAIT_LOOP_PER_SECOND); ++ nanosleep(&duration, NULL); ++ } ++ log_debug("waiting for '%s' failed", file); ++ return -1; ++} ++ + static void attr_subst_subdir(char *attr, size_t len) { + const char *pos, *tail, *path; + _cleanup_closedir_ DIR *dir = NULL; +@@ -1284,7 +1319,12 @@ static void add_rule(struct udev_rules *rules, char *line, + rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd); + } else + LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr); ++ } else if (streq(key, "WAIT_FOR") || streq(key, "WAIT_FOR_SYSFS")) { ++ if (op == OP_REMOVE) ++ LOG_AND_RETURN("invalid %s operation", key); + ++ rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); ++ continue; + } else if (streq(key, "LABEL")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); +@@ -1838,6 +1878,16 @@ void udev_rules_apply_to_event(struct udev_rules *rules, + if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) + goto nomatch; + break; ++ case TK_M_WAITFOR: { ++ char filename[UTIL_PATH_SIZE]; ++ int found; ++ ++ udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false); ++ found = (wait_for_file(event->dev, filename, 10) == 0); ++ if (!found && (cur->key.op != OP_NOMATCH)) ++ goto nomatch; ++ break; ++ } + case TK_M_ATTR: + if (match_attr(rules, event->dev, event, cur) != 0) + goto nomatch; +diff --git a/test/rule-syntax-check.py b/test/rule-syntax-check.py +index dfb06d9ed9..706d93632e 100755 +--- a/test/rule-syntax-check.py ++++ b/test/rule-syntax-check.py +@@ -18,7 +18,7 @@ if not rules_files: + quoted_string_re = r'"(?:[^\\"]|\\.)*"' + no_args_tests = re.compile(r'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|PROGRAM|RESULT|TEST)\s*(?:=|!)=\s*' + quoted_string_re + '$') + args_tests = re.compile(r'(ATTRS?|ENV|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*' + quoted_string_re + '$') +-no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|RUN|LABEL|GOTO|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*' + quoted_string_re + '$') ++no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|RUN|LABEL|GOTO|WAIT_FOR|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*' + quoted_string_re + '$') + args_assign = re.compile(r'(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*' + quoted_string_re + '$') + # Find comma-separated groups, but allow commas that are inside quoted strings. + # Using quoted_string_re + '?' so that strings missing the last double quote diff --git a/SOURCES/0017-net_setup_link-allow-renaming-interfaces-that-were-r.patch b/SOURCES/0017-net_setup_link-allow-renaming-interfaces-that-were-r.patch new file mode 100644 index 0000000..dfa948b --- /dev/null +++ b/SOURCES/0017-net_setup_link-allow-renaming-interfaces-that-were-r.patch @@ -0,0 +1,22 @@ +From ab0228c3d6ceba20cf89ceb1b16b7e314aaaf989 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 7 Aug 2018 10:38:33 +0200 +Subject: [PATCH] net_setup_link: allow renaming interfaces that were renamed + previously + +--- + src/udev/net/link-config.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c +index cec4f4f779..5113586457 100644 +--- a/src/udev/net/link-config.c ++++ b/src/udev/net/link-config.c +@@ -306,7 +306,6 @@ static bool should_rename(struct udev_device *device, bool respect_predictable) + + switch (type) { + case NET_NAME_USER: +- case NET_NAME_RENAMED: + /* these were already named by userspace, do not touch again */ + return false; + case NET_NAME_PREDICTABLE: diff --git a/SOURCES/0018-units-drop-DynamicUser-yes-from-systemd-resolved.ser.patch b/SOURCES/0018-units-drop-DynamicUser-yes-from-systemd-resolved.ser.patch new file mode 100644 index 0000000..a42d3c7 --- /dev/null +++ b/SOURCES/0018-units-drop-DynamicUser-yes-from-systemd-resolved.ser.patch @@ -0,0 +1,23 @@ +From b61e8046ebcb28225423fc0073183d68d4c577c4 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 9 Aug 2018 15:28:44 +0200 +Subject: [PATCH] units: drop DynamicUser=yes from systemd-resolved.service + +We don't really need DynamicUser since we add systemd-resolve user +from rpm script +--- + units/systemd-resolved.service.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in +index 9982ecebff..aaed406ab2 100644 +--- a/units/systemd-resolved.service.in ++++ b/units/systemd-resolved.service.in +@@ -26,7 +26,6 @@ RestartSec=0 + ExecStart=!!@rootlibexecdir@/systemd-resolved + WatchdogSec=3min + User=systemd-resolve +-DynamicUser=yes + CapabilityBoundingSet=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + AmbientCapabilities=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + PrivateDevices=yes diff --git a/SOURCES/0019-journal-remove-journal-audit-socket.patch b/SOURCES/0019-journal-remove-journal-audit-socket.patch new file mode 100644 index 0000000..bb749c8 --- /dev/null +++ b/SOURCES/0019-journal-remove-journal-audit-socket.patch @@ -0,0 +1,73 @@ +From 8618ef2fb30b4139c9bec4e45fb499cd8192a87f Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 9 Aug 2018 23:23:00 +0200 +Subject: [PATCH] journal: remove journal audit socket + +Resolves: #1614554 +--- + units/meson.build | 2 -- + units/systemd-journald-audit.socket | 22 ---------------------- + units/systemd-journald.service.in | 4 ++-- + 3 files changed, 2 insertions(+), 26 deletions(-) + delete mode 100644 units/systemd-journald-audit.socket + +diff --git a/units/meson.build b/units/meson.build +index e4ac6ced64..e54a84ccbf 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -89,8 +89,6 @@ units = [ + 'sockets.target.wants/'], + ['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'], + ['systemd-journal-remote.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'], +- ['systemd-journald-audit.socket', '', +- 'sockets.target.wants/'], + ['systemd-journald-dev-log.socket', '', + 'sockets.target.wants/'], + ['systemd-journald.socket', '', +diff --git a/units/systemd-journald-audit.socket b/units/systemd-journald-audit.socket +deleted file mode 100644 +index cb8b774963..0000000000 +--- a/units/systemd-journald-audit.socket ++++ /dev/null +@@ -1,22 +0,0 @@ +-# SPDX-License-Identifier: LGPL-2.1+ +-# +-# This file is part of systemd. +-# +-# systemd is free software; you can redistribute it and/or modify it +-# under the terms of the GNU Lesser General Public License as published by +-# the Free Software Foundation; either version 2.1 of the License, or +-# (at your option) any later version. +- +-[Unit] +-Description=Journal Audit Socket +-Documentation=man:systemd-journald.service(8) man:journald.conf(5) +-DefaultDependencies=no +-Before=sockets.target +-ConditionSecurity=audit +-ConditionCapability=CAP_AUDIT_READ +- +-[Socket] +-Service=systemd-journald.service +-ReceiveBuffer=128M +-ListenNetlink=audit 1 +-PassCredentials=yes +diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in +index 52939e6820..8f5021d0de 100644 +--- a/units/systemd-journald.service.in ++++ b/units/systemd-journald.service.in +@@ -12,12 +12,12 @@ Description=Journal Service + Documentation=man:systemd-journald.service(8) man:journald.conf(5) + DefaultDependencies=no + Requires=systemd-journald.socket +-After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket syslog.socket ++After=systemd-journald.socket systemd-journald-dev-log.socket syslog.socket + Before=sysinit.target + + [Service] + Type=notify +-Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket ++Sockets=systemd-journald.socket systemd-journald-dev-log.socket + ExecStart=@rootlibexecdir@/systemd-journald + Restart=always + RestartSec=0 diff --git a/SOURCES/0020-bus-move-BUS_DONT_DESTROY-calls-after-asserts.patch b/SOURCES/0020-bus-move-BUS_DONT_DESTROY-calls-after-asserts.patch new file mode 100644 index 0000000..0141ff4 --- /dev/null +++ b/SOURCES/0020-bus-move-BUS_DONT_DESTROY-calls-after-asserts.patch @@ -0,0 +1,117 @@ +From c6903d1b42d1773fda4df6676618489ad760a2a1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 18 Jul 2018 12:16:33 +0200 +Subject: [PATCH] bus: move BUS_DONT_DESTROY calls after asserts + +It's not useful to bump the reference count before checking if the object is +NULL. Thanks to d40f5cc498 we can do this ;). + +Related to https://bugzilla.redhat.com/show_bug.cgi?id=1576084, +https://bugzilla.redhat.com/show_bug.cgi?id=1575340, +https://bugzilla.redhat.com/show_bug.cgi?id=1575350. I'm not sure why those two +people hit this code path, while most people don't. At least we won't abort. + +(cherry picked from commit 7ae8edcd03f74da123298330b76c3fc5425042ef) + +Resolves: #1610397 +--- + src/libsystemd/sd-bus/bus-objects.c | 15 ++++++++------- + src/libsystemd/sd-bus/sd-bus.c | 3 ++- + 2 files changed, 10 insertions(+), 8 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c +index 9609834fa9..a18ff88b07 100644 +--- a/src/libsystemd/sd-bus/bus-objects.c ++++ b/src/libsystemd/sd-bus/bus-objects.c +@@ -2090,7 +2090,6 @@ _public_ int sd_bus_emit_properties_changed_strv( + const char *interface, + char **names) { + +- BUS_DONT_DESTROY(bus); + bool found_interface = false; + char *prefix; + int r; +@@ -2111,6 +2110,8 @@ _public_ int sd_bus_emit_properties_changed_strv( + if (names && names[0] == NULL) + return 0; + ++ BUS_DONT_DESTROY(bus); ++ + do { + bus->nodes_modified = false; + +@@ -2310,8 +2311,6 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p + } + + _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { +- BUS_DONT_DESTROY(bus); +- + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; +@@ -2341,6 +2340,8 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { + if (r == 0) + return -ESRCH; + ++ BUS_DONT_DESTROY(bus); ++ + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); +@@ -2481,8 +2482,6 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char + } + + _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { +- BUS_DONT_DESTROY(bus); +- + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + int r; +@@ -2512,6 +2511,8 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { + if (r == 0) + return -ESRCH; + ++ BUS_DONT_DESTROY(bus); ++ + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); +@@ -2645,8 +2646,6 @@ static int interfaces_added_append_one( + } + + _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) { +- BUS_DONT_DESTROY(bus); +- + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + struct node *object_manager; + char **i; +@@ -2669,6 +2668,8 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch + if (r == 0) + return -ESRCH; + ++ BUS_DONT_DESTROY(bus); ++ + do { + bus->nodes_modified = false; + m = sd_bus_message_unref(m); +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 089b51a6d9..7f03528b89 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -2883,7 +2883,6 @@ finish: + } + + static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) { +- BUS_DONT_DESTROY(bus); + int r; + + /* Returns 0 when we didn't do anything. This should cause the +@@ -2899,6 +2898,8 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit + assert_return(!bus->current_message, -EBUSY); + assert(!bus->current_slot); + ++ BUS_DONT_DESTROY(bus); ++ + switch (bus->state) { + + case BUS_UNSET: diff --git a/SOURCES/0021-random-seed-raise-POOL_SIZE_MIN-constant-to-1024.patch b/SOURCES/0021-random-seed-raise-POOL_SIZE_MIN-constant-to-1024.patch new file mode 100644 index 0000000..e860755 --- /dev/null +++ b/SOURCES/0021-random-seed-raise-POOL_SIZE_MIN-constant-to-1024.patch @@ -0,0 +1,23 @@ +From 56f614a5d6305dc1d304c30438db5b394d16e2da Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 12 Oct 2018 13:58:34 +0000 +Subject: [PATCH] random-seed: raise POOL_SIZE_MIN constant to 1024 + +Resolves: #1619268 +--- + src/random-seed/random-seed.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c +index 223b56306c..adc9f298c1 100644 +--- a/src/random-seed/random-seed.c ++++ b/src/random-seed/random-seed.c +@@ -14,7 +14,7 @@ + #include "string-util.h" + #include "util.h" + +-#define POOL_SIZE_MIN 512 ++#define POOL_SIZE_MIN 1024 + + int main(int argc, char *argv[]) { + _cleanup_close_ int seed_fd = -1, random_fd = -1; diff --git a/SOURCES/0022-cryptsetup-add-support-for-sector-size-option-9936.patch b/SOURCES/0022-cryptsetup-add-support-for-sector-size-option-9936.patch new file mode 100644 index 0000000..b66f172 --- /dev/null +++ b/SOURCES/0022-cryptsetup-add-support-for-sector-size-option-9936.patch @@ -0,0 +1,119 @@ +From a046230cfb7e02938e3ad2ac85515636b319651e Mon Sep 17 00:00:00 2001 +From: Dimitri John Ledkov +Date: Wed, 29 Aug 2018 15:38:09 +0100 +Subject: [PATCH] cryptsetup: add support for sector-size= option (#9936) + +Bug-Ubuntu: https://launchpad.net/bugs/1776626 + +Closes #8881. + +(cherry picked from commit a9fc640671ef60ac949f1ace6fa687ff242fc233) + +Resolves: #1572563 +--- + man/crypttab.xml | 9 +++++++++ + meson.build | 6 ++++++ + src/cryptsetup/cryptsetup.c | 30 ++++++++++++++++++++++++++++++ + 3 files changed, 45 insertions(+) + +diff --git a/man/crypttab.xml b/man/crypttab.xml +index dcaf03d2ca..3574ce00da 100644 +--- a/man/crypttab.xml ++++ b/man/crypttab.xml +@@ -250,6 +250,15 @@ + option. + + ++ ++ ++ ++ Specifies the sector size in bytes. See ++ cryptsetup8 ++ for possible values and the default value of this ++ option. ++ ++ + + + +diff --git a/meson.build b/meson.build +index a0e7240708..f308db2631 100644 +--- a/meson.build ++++ b/meson.build +@@ -927,11 +927,17 @@ if want_libcryptsetup != 'false' and not fuzzer_build + version : '>= 1.6.0', + required : want_libcryptsetup == 'true') + have = libcryptsetup.found() ++ have_sector = cc.has_member( ++ 'struct crypt_params_plain', ++ 'sector_size', ++ prefix : '#include ') + else + have = false ++ have_sector = false + libcryptsetup = [] + endif + conf.set10('HAVE_LIBCRYPTSETUP', have) ++conf.set10('HAVE_LIBCRYPTSETUP_SECTOR_SIZE', have_sector) + + want_libcurl = get_option('libcurl') + if want_libcurl != 'false' and not fuzzer_build +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 832168184a..87008cb969 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -23,10 +23,14 @@ + + /* internal helper */ + #define ANY_LUKS "LUKS" ++/* as in src/cryptsetup.h */ ++#define CRYPT_SECTOR_SIZE 512 ++#define CRYPT_MAX_SECTOR_SIZE 4096 + + static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */ + static char *arg_cipher = NULL; + static unsigned arg_key_size = 0; ++static unsigned arg_sector_size = CRYPT_SECTOR_SIZE; + static int arg_key_slot = CRYPT_ANY_SLOT; + static unsigned arg_keyfile_size = 0; + static uint64_t arg_keyfile_offset = 0; +@@ -86,6 +90,29 @@ static int parse_one_option(const char *option) { + + arg_key_size /= 8; + ++ } else if ((val = startswith(option, "sector-size="))) { ++ ++#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE ++ r = safe_atou(val, &arg_sector_size); ++ if (r < 0) { ++ log_error_errno(r, "Failed to parse %s, ignoring: %m", option); ++ return 0; ++ } ++ ++ if (arg_sector_size % 2) { ++ log_error("sector-size= not a multiple of 2, ignoring."); ++ return 0; ++ } ++ ++ if (arg_sector_size < CRYPT_SECTOR_SIZE || arg_sector_size > CRYPT_MAX_SECTOR_SIZE) { ++ log_error("sector-size= is outside of %u and %u, ignoring.", CRYPT_SECTOR_SIZE, CRYPT_MAX_SECTOR_SIZE); ++ return 0; ++ } ++#else ++ log_error("sector-size= is not supported, compiled with old libcryptsetup."); ++ return 0; ++#endif ++ + } else if ((val = startswith(option, "key-slot="))) { + + arg_type = ANY_LUKS; +@@ -471,6 +498,9 @@ static int attach_luks_or_plain(struct crypt_device *cd, + struct crypt_params_plain params = { + .offset = arg_offset, + .skip = arg_skip, ++#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE ++ .sector_size = arg_sector_size, ++#endif + }; + const char *cipher, *cipher_mode; + _cleanup_free_ char *truncated_cipher = NULL; diff --git a/SOURCES/0023-cryptsetup-do-not-define-arg_sector_size-if-libgcryp.patch b/SOURCES/0023-cryptsetup-do-not-define-arg_sector_size-if-libgcryp.patch new file mode 100644 index 0000000..3413a18 --- /dev/null +++ b/SOURCES/0023-cryptsetup-do-not-define-arg_sector_size-if-libgcryp.patch @@ -0,0 +1,29 @@ +From 96b6171376bfdb7417143a2026beda059fe3e22f Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Sat, 1 Sep 2018 23:47:46 +0900 +Subject: [PATCH] cryptsetup: do not define arg_sector_size if libgcrypt is + v1.x (#9990) + +Follow-up for #9936. + +(cherry picked from commit 645461f0cf6ec91e5b0b571559fb4cc4898192bc) + +Related: #1572563 +--- + src/cryptsetup/cryptsetup.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 87008cb969..abeba44ee8 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -30,7 +30,9 @@ + static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */ + static char *arg_cipher = NULL; + static unsigned arg_key_size = 0; ++#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE + static unsigned arg_sector_size = CRYPT_SECTOR_SIZE; ++#endif + static int arg_key_slot = CRYPT_ANY_SLOT; + static unsigned arg_keyfile_size = 0; + static uint64_t arg_keyfile_offset = 0; diff --git a/SOURCES/0024-units-don-t-enable-per-service-IP-firewall-by-defaul.patch b/SOURCES/0024-units-don-t-enable-per-service-IP-firewall-by-defaul.patch new file mode 100644 index 0000000..f2bd378 --- /dev/null +++ b/SOURCES/0024-units-don-t-enable-per-service-IP-firewall-by-defaul.patch @@ -0,0 +1,112 @@ +From e143339ac712f745727951973417ce93b5d06d78 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 12 Oct 2018 14:50:09 +0000 +Subject: [PATCH] units: don't enable per-service IP firewall by default + +Resolves: #1630219 +--- + units/systemd-coredump@.service.in | 1 - + units/systemd-hostnamed.service.in | 1 - + units/systemd-journald.service.in | 1 - + units/systemd-localed.service.in | 1 - + units/systemd-logind.service.in | 1 - + units/systemd-machined.service.in | 1 - + units/systemd-portabled.service.in | 1 - + units/systemd-timedated.service.in | 1 - + units/systemd-udevd.service.in | 1 - + 9 files changed, 9 deletions(-) + +diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in +index 215696ecd1..68a68a5055 100644 +--- a/units/systemd-coredump@.service.in ++++ b/units/systemd-coredump@.service.in +@@ -37,5 +37,4 @@ SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any + StateDirectory=systemd/coredump +diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in +index da74b4fe8b..4e5470dd29 100644 +--- a/units/systemd-hostnamed.service.in ++++ b/units/systemd-hostnamed.service.in +@@ -33,5 +33,4 @@ SystemCallFilter=@system-service sethostname + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any + ReadWritePaths=/etc +diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in +index 8f5021d0de..2d5fd0120d 100644 +--- a/units/systemd-journald.service.in ++++ b/units/systemd-journald.service.in +@@ -33,7 +33,6 @@ SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any + + # Increase the default a bit in order to allow many simultaneous + # services being run since we keep one fd open per service. Also, when +diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in +index a24e61a0cd..ce043db154 100644 +--- a/units/systemd-localed.service.in ++++ b/units/systemd-localed.service.in +@@ -33,5 +33,4 @@ SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any + ReadWritePaths=/etc +diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in +index 5e090bcf23..6953fac55b 100644 +--- a/units/systemd-logind.service.in ++++ b/units/systemd-logind.service.in +@@ -34,7 +34,6 @@ SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any + FileDescriptorStoreMax=512 + + # Increase the default a bit in order to allow many simultaneous +diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in +index 1200a90a61..dec2c4b0dc 100644 +--- a/units/systemd-machined.service.in ++++ b/units/systemd-machined.service.in +@@ -27,7 +27,6 @@ SystemCallFilter=@system-service @mount + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-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 +diff --git a/units/systemd-portabled.service.in b/units/systemd-portabled.service.in +index a868f61dba..64f14071e8 100644 +--- a/units/systemd-portabled.service.in ++++ b/units/systemd-portabled.service.in +@@ -23,4 +23,3 @@ RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 + SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any +diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in +index 906bb4326c..662b39557a 100644 +--- a/units/systemd-timedated.service.in ++++ b/units/systemd-timedated.service.in +@@ -31,5 +31,4 @@ SystemCallFilter=@system-service @clock + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any + ReadWritePaths=/etc +diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in +index 6a3814e5d9..fd9ead3bb8 100644 +--- a/units/systemd-udevd.service.in ++++ b/units/systemd-udevd.service.in +@@ -33,4 +33,3 @@ SystemCallFilter=@system-service @module @raw-io + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes +-IPAddressDeny=any diff --git a/SOURCES/0025-bus-message-do-not-crash-on-message-with-a-string-of.patch b/SOURCES/0025-bus-message-do-not-crash-on-message-with-a-string-of.patch new file mode 100644 index 0000000..6110b2a --- /dev/null +++ b/SOURCES/0025-bus-message-do-not-crash-on-message-with-a-string-of.patch @@ -0,0 +1,45 @@ +From 87922b7adc47f311e89b21e37b26ee300a401e1d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 9 Jul 2018 13:21:44 +0200 +Subject: [PATCH] bus-message: do not crash on message with a string of zero + length + +We'd calculate the "real" length of the string as 'item_size - 1', which does +not work out well when item_size == 0. + +(cherry picked from commit 81b6e63029eefcb0ec03a3a7c248490e38106073) + +Resolves: #1635439 +--- + src/libsystemd/sd-bus/bus-message.c | 6 ++++++ + .../crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e | Bin 0 -> 51 bytes + 2 files changed, 6 insertions(+) + create mode 100644 test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 8d92bc2002..381034f5f8 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -3312,6 +3312,12 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { + if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { + bool ok; + ++ /* D-Bus spec: The marshalling formats for the string-like types all end ++ * with a single zero (NUL) byte, but that byte is not considered to be part ++ * of the text. */ ++ if (c->item_size == 0) ++ return -EBADMSG; ++ + r = message_peek_body(m, &rindex, 1, c->item_size, &q); + if (r < 0) + return r; +diff --git a/test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e b/test/fuzz/fuzz-bus-message/crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e +new file mode 100644 +index 0000000000000000000000000000000000000000..4488f0a6c685b5d43eddbe41a0c6a3b6be9b02e2 +GIT binary patch +literal 51 +fcmc~1WMC4sJpJnr13KV`0|t%6q+%$@&=ddw)CUPg + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0026-Introduce-free_and_strndup-and-use-it-in-bus-message.patch b/SOURCES/0026-Introduce-free_and_strndup-and-use-it-in-bus-message.patch new file mode 100644 index 0000000..90fedb8 --- /dev/null +++ b/SOURCES/0026-Introduce-free_and_strndup-and-use-it-in-bus-message.patch @@ -0,0 +1,279 @@ +From 26de3af817b0c5746cb61b798ae8e138e01ea17c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 9 Jul 2018 07:03:01 +0200 +Subject: [PATCH] Introduce free_and_strndup and use it in bus-message.c + +v2: fix error in free_and_strndup() + +When the orignal and copied message were the same, but shorter than specified +length l, memory read past the end of the buffer would be performed. A test +case is included: a string that had an embedded NUL ("q\0") is used to replace +"q". + +v3: Fix one more bug in free_and_strndup and add tests. + +v4: Some style fixed based on review, one more use of free_and_replace, and +make the tests more comprehensive. + +(cherry picked from commit 7f546026abbdc56c453a577e52d57159458c3e9c) + +Resolves: #1635428 +--- + src/basic/string-util.c | 28 +++++++- + src/basic/string-util.h | 1 + + src/libsystemd/sd-bus/bus-message.c | 34 ++++------ + src/test/test-string-util.c | 62 ++++++++++++++++++ + ...h-b88ad9ecf4aacf4a0caca5b5543953265367f084 | Bin 0 -> 32 bytes + 5 files changed, 103 insertions(+), 22 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084 + +diff --git a/src/basic/string-util.c b/src/basic/string-util.c +index 0a40683493..dfa739996f 100644 +--- a/src/basic/string-util.c ++++ b/src/basic/string-util.c +@@ -1004,7 +1004,7 @@ int free_and_strdup(char **p, const char *s) { + + assert(p); + +- /* Replaces a string pointer with an strdup()ed new string, ++ /* Replaces a string pointer with a strdup()ed new string, + * possibly freeing the old one. */ + + if (streq_ptr(*p, s)) +@@ -1023,6 +1023,32 @@ int free_and_strdup(char **p, const char *s) { + return 1; + } + ++int free_and_strndup(char **p, const char *s, size_t l) { ++ char *t; ++ ++ assert(p); ++ assert(s || l == 0); ++ ++ /* Replaces a string pointer with a strndup()ed new string, ++ * freeing the old one. */ ++ ++ if (!*p && !s) ++ return 0; ++ ++ if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0')) ++ return 0; ++ ++ if (s) { ++ t = strndup(s, l); ++ if (!t) ++ return -ENOMEM; ++ } else ++ t = NULL; ++ ++ free_and_replace(*p, t); ++ return 1; ++} ++ + #if !HAVE_EXPLICIT_BZERO + /* + * Pointer to memset is volatile so that compiler must de-reference +diff --git a/src/basic/string-util.h b/src/basic/string-util.h +index c0cc4e78d7..96a9260f93 100644 +--- a/src/basic/string-util.h ++++ b/src/basic/string-util.h +@@ -176,6 +176,7 @@ char *strrep(const char *s, unsigned n); + int split_pair(const char *s, const char *sep, char **l, char **r); + + int free_and_strdup(char **p, const char *s); ++int free_and_strndup(char **p, const char *s, size_t l); + + /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ + static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 381034f5f8..7c8bad2bdd 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -4175,20 +4175,19 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + + if (contents) { + size_t l; +- char *sig; + + r = signature_element_length(c->signature+c->index+1, &l); + if (r < 0) + return r; + +- assert(l >= 1); ++ /* signature_element_length does verification internally */ + +- sig = strndup(c->signature + c->index + 1, l); +- if (!sig) ++ assert(l >= 1); ++ if (free_and_strndup(&c->peeked_signature, ++ c->signature + c->index + 1, l) < 0) + return -ENOMEM; + +- free(c->peeked_signature); +- *contents = c->peeked_signature = sig; ++ *contents = c->peeked_signature; + } + + if (type) +@@ -4201,19 +4200,17 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + + if (contents) { + size_t l; +- char *sig; + + r = signature_element_length(c->signature+c->index, &l); + if (r < 0) + return r; + + assert(l >= 2); +- sig = strndup(c->signature + c->index + 1, l - 2); +- if (!sig) ++ if (free_and_strndup(&c->peeked_signature, ++ c->signature + c->index + 1, l - 2) < 0) + return -ENOMEM; + +- free(c->peeked_signature); +- *contents = c->peeked_signature = sig; ++ *contents = c->peeked_signature; + } + + if (type) +@@ -4253,9 +4250,8 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + if (k > c->item_size) + return -EBADMSG; + +- free(c->peeked_signature); +- c->peeked_signature = strndup((char*) q + 1, k - 1); +- if (!c->peeked_signature) ++ if (free_and_strndup(&c->peeked_signature, ++ (char*) q + 1, k - 1) < 0) + return -ENOMEM; + + if (!signature_is_valid(c->peeked_signature, true)) +@@ -5085,25 +5081,21 @@ int bus_message_parse_fields(sd_bus_message *m) { + + if (*p == 0) { + size_t l; +- char *c; + + /* We found the beginning of the signature + * string, yay! We require the body to be a + * structure, so verify it and then strip the + * opening/closing brackets. */ + +- l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz); ++ l = (char*) m->footer + m->footer_accessible - p - (1 + sz); + if (l < 2 || + p[1] != SD_BUS_TYPE_STRUCT_BEGIN || + p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END) + return -EBADMSG; + +- c = strndup(p + 1 + 1, l - 2); +- if (!c) ++ if (free_and_strndup(&m->root_container.signature, ++ p + 1 + 1, l - 2) < 0) + return -ENOMEM; +- +- free(m->root_container.signature); +- m->root_container.signature = c; + break; + } + +diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c +index 3e72ce2c0a..43a6b14c34 100644 +--- a/src/test/test-string-util.c ++++ b/src/test/test-string-util.c +@@ -5,6 +5,7 @@ + #include "macro.h" + #include "string-util.h" + #include "strv.h" ++#include "tests.h" + #include "utf8.h" + + static void test_string_erase(void) { +@@ -30,6 +31,64 @@ static void test_string_erase(void) { + assert_se(x[9] == '\0'); + } + ++static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) { ++ int r; ++ ++ log_debug("%s: \"%s\", \"%s\", %zd (expect \"%s\", %s)", ++ __func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change)); ++ ++ r = free_and_strndup(t, src, l); ++ assert_se(streq_ptr(*t, expected)); ++ assert_se(r == change); /* check that change occurs only when necessary */ ++} ++ ++static void test_free_and_strndup(void) { ++ static const struct test_case { ++ const char *src; ++ size_t len; ++ const char *expected; ++ } cases[] = { ++ {"abc", 0, ""}, ++ {"abc", 0, ""}, ++ {"abc", 1, "a"}, ++ {"abc", 2, "ab"}, ++ {"abc", 3, "abc"}, ++ {"abc", 4, "abc"}, ++ {"abc", 5, "abc"}, ++ {"abc", 5, "abc"}, ++ {"abc", 4, "abc"}, ++ {"abc", 3, "abc"}, ++ {"abc", 2, "ab"}, ++ {"abc", 1, "a"}, ++ {"abc", 0, ""}, ++ ++ {"", 0, ""}, ++ {"", 1, ""}, ++ {"", 2, ""}, ++ {"", 0, ""}, ++ {"", 1, ""}, ++ {"", 2, ""}, ++ {"", 2, ""}, ++ {"", 1, ""}, ++ {"", 0, ""}, ++ ++ {NULL, 0, NULL}, ++ ++ {"foo", 3, "foo"}, ++ {"foobar", 6, "foobar"}, ++ }; ++ ++ _cleanup_free_ char *t = NULL; ++ const char *prev_expected = t; ++ ++ for (unsigned i = 0; i < ELEMENTSOF(cases); i++) { ++ test_free_and_strndup_one(&t, ++ cases[i].src, cases[i].len, cases[i].expected, ++ !streq_ptr(cases[i].expected, prev_expected)); ++ prev_expected = t; ++ } ++} ++ + static void test_ascii_strcasecmp_n(void) { + + assert_se(ascii_strcasecmp_n("", "", 0) == 0); +@@ -497,7 +556,10 @@ static void test_memory_startswith(void) { + } + + int main(int argc, char *argv[]) { ++ test_setup_logging(LOG_DEBUG); ++ + test_string_erase(); ++ test_free_and_strndup(); + test_ascii_strcasecmp_n(); + test_ascii_strcasecmp_nn(); + test_cellescape(); +diff --git a/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084 b/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084 +new file mode 100644 +index 0000000000000000000000000000000000000000..52469650b5498a45d5d95bd9d933c989cfb47ca7 +GIT binary patch +literal 32 +ccmd1#|DTBg0(2Mzp)7_%AVVXuuuM|`09r!?!~g&Q + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0027-tests-backport-test_setup_logging.patch b/SOURCES/0027-tests-backport-test_setup_logging.patch new file mode 100644 index 0000000..bc669a1 --- /dev/null +++ b/SOURCES/0027-tests-backport-test_setup_logging.patch @@ -0,0 +1,34 @@ +From ab6a1bdf3519d4344dee4e0225c74fc1198c8a60 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 15 Oct 2018 10:54:11 +0000 +Subject: [PATCH] tests: backport test_setup_logging() + +Related: #1635428 +--- + src/shared/tests.c | 6 ++++++ + src/shared/tests.h | 1 + + 2 files changed, 7 insertions(+) + +diff --git a/src/shared/tests.c b/src/shared/tests.c +index 6b3df0aa07..b10343650f 100644 +--- a/src/shared/tests.c ++++ b/src/shared/tests.c +@@ -54,3 +54,9 @@ const char* get_testdata_dir(const char *suffix) { + strncpy(testdir + strlen(testdir), suffix, sizeof(testdir) - strlen(testdir) - 1); + return testdir; + } ++ ++void test_setup_logging(int level) { ++ log_set_max_level(level); ++ log_parse_environment(); ++ log_open(); ++} +diff --git a/src/shared/tests.h b/src/shared/tests.h +index b88135ed93..cad21169f8 100644 +--- a/src/shared/tests.h ++++ b/src/shared/tests.h +@@ -3,3 +3,4 @@ + + char* setup_fake_runtime_dir(void); + const char* get_testdata_dir(const char *suffix); ++void test_setup_logging(int level); diff --git a/SOURCES/0028-journal-change-support-URL-shown-in-the-catalog-entr.patch b/SOURCES/0028-journal-change-support-URL-shown-in-the-catalog-entr.patch new file mode 100644 index 0000000..65b1c32 --- /dev/null +++ b/SOURCES/0028-journal-change-support-URL-shown-in-the-catalog-entr.patch @@ -0,0 +1,23 @@ +From 80d5f0e2057717e9e5588edcabac95b8c238795c Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 15 Oct 2018 10:55:50 +0000 +Subject: [PATCH] journal: change support URL shown in the catalog entries + +Resolves: #1550548 +--- + meson_options.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/meson_options.txt b/meson_options.txt +index 16c1f2b2fa..ab2a658713 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -205,7 +205,7 @@ option('ntp-servers', type : 'string', + value : 'time1.google.com time2.google.com time3.google.com time4.google.com') + option('support-url', type : 'string', + description : 'the support URL to show in catalog entries included in systemd', +- value : 'https://lists.freedesktop.org/mailman/listinfo/systemd-devel') ++ value : 'https://access.redhat.com/support') + option('www-target', type : 'string', + description : 'the address and dir to upload docs too', + value : 'www.freedesktop.org:/srv/www.freedesktop.org/www/software/systemd') diff --git a/SOURCES/0029-resolved-create-etc-resolv.conf-symlink-at-runtime.patch b/SOURCES/0029-resolved-create-etc-resolv.conf-symlink-at-runtime.patch new file mode 100644 index 0000000..e5aeedd --- /dev/null +++ b/SOURCES/0029-resolved-create-etc-resolv.conf-symlink-at-runtime.patch @@ -0,0 +1,48 @@ +From e0f2dd42fb02aa5767d38714c95ac10fb683ad67 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 11 Mar 2016 17:06:17 -0500 +Subject: [PATCH] resolved: create /etc/resolv.conf symlink at runtime + +If the symlink doesn't exists, and we are being started, let's +create it to provie name resolution. + +If it exists, do nothing. In particular, if it is a broken symlink, +we cannot really know if the administator configured it to point to +a location used by some service that hasn't started yet, so we +don't touch it in that case either. + +https://bugzilla.redhat.com/show_bug.cgi?id=1313085 +--- + src/resolve/resolved.c | 4 ++++ + tmpfiles.d/etc.conf.m4 | 3 --- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c +index c01e53e9da..f3d96df458 100644 +--- a/src/resolve/resolved.c ++++ b/src/resolve/resolved.c +@@ -53,6 +53,10 @@ int main(int argc, char *argv[]) { + /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all + * privileges are already dropped. */ + if (getuid() == 0) { ++ r = symlink("../run/systemd/resolve/resolv.conf", "/etc/resolv.conf"); ++ if (r < 0 && errno != EEXIST) ++ log_warning_errno(errno, ++ "Could not create /etc/resolv.conf symlink: %m"); + + /* Drop privileges, but keep three caps. Note that we drop those too, later on (see below) */ + r = drop_privileges(uid, gid, +diff --git a/tmpfiles.d/etc.conf.m4 b/tmpfiles.d/etc.conf.m4 +index df8d42101c..928105ea8d 100644 +--- a/tmpfiles.d/etc.conf.m4 ++++ b/tmpfiles.d/etc.conf.m4 +@@ -13,9 +13,6 @@ L+ /etc/mtab - - - - ../proc/self/mounts + m4_ifdef(`HAVE_SMACK_RUN_LABEL', + t /etc/mtab - - - - security.SMACK64=_ + )m4_dnl +-m4_ifdef(`ENABLE_RESOLVE', +-L! /etc/resolv.conf - - - - ../run/systemd/resolve/stub-resolv.conf +-)m4_dnl + C /etc/nsswitch.conf - - - - + m4_ifdef(`HAVE_PAM', + C /etc/pam.d - - - - diff --git a/SOURCES/0030-dissect-image-use-right-comparison-function.patch b/SOURCES/0030-dissect-image-use-right-comparison-function.patch new file mode 100644 index 0000000..f35e9eb --- /dev/null +++ b/SOURCES/0030-dissect-image-use-right-comparison-function.patch @@ -0,0 +1,27 @@ +From e615b80f3fda82ac7fe628800a9ff2103788bd05 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 9 Oct 2018 13:50:55 +0200 +Subject: [PATCH] dissect-image: use right comparison function + +fstype can be NULL here. + +(cherry picked from commit 4db1879acdc0b853e1a7e6e650b6feb917175fac) + +Resolves: #1602706 +--- + src/shared/dissect-image.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c +index fa1cf26ee1..e076c8e7db 100644 +--- a/src/shared/dissect-image.c ++++ b/src/shared/dissect-image.c +@@ -230,7 +230,7 @@ int dissect_image( + .node = TAKE_PTR(n), + }; + +- m->encrypted = streq(fstype, "crypto_LUKS"); ++ m->encrypted = streq_ptr(fstype, "crypto_LUKS"); + + *ret = TAKE_PTR(m); + diff --git a/SOURCES/0031-login-avoid-leak-of-name-returned-by-uid_to_name.patch b/SOURCES/0031-login-avoid-leak-of-name-returned-by-uid_to_name.patch new file mode 100644 index 0000000..e1ea815 --- /dev/null +++ b/SOURCES/0031-login-avoid-leak-of-name-returned-by-uid_to_name.patch @@ -0,0 +1,60 @@ +From 8fdca31b41a6470ceda8e0a84f90a1e5ca28aa5c Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 9 Oct 2018 17:26:19 +0200 +Subject: [PATCH] login: avoid leak of name returned by uid_to_name() + +(cherry picked from commit e99742ef3e9d847da04e71fec0eb426063b25068) + +Resolves: #1602706 +--- + src/login/logind-dbus.c | 4 +++- + src/login/logind-utmp.c | 6 +++--- + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index 13298cc855..dca7f4a30f 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -2155,6 +2155,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd + + if (cancelled && m->enable_wall_messages) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; ++ _cleanup_free_ char *username = NULL; + const char *tty = NULL; + uid_t uid = 0; + int r; +@@ -2165,8 +2166,9 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd + (void) sd_bus_creds_get_tty(creds, &tty); + } + ++ username = uid_to_name(uid); + utmp_wall("The system shutdown has been cancelled", +- uid_to_name(uid), tty, logind_wall_tty_filter, m); ++ username, tty, logind_wall_tty_filter, m); + } + + return sd_bus_reply_method_return(message, "b", cancelled); +diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c +index 71ebdfcfb1..8bdd4ab6bf 100644 +--- a/src/login/logind-utmp.c ++++ b/src/login/logind-utmp.c +@@ -61,7 +61,7 @@ bool logind_wall_tty_filter(const char *tty, void *userdata) { + + static int warn_wall(Manager *m, usec_t n) { + char date[FORMAT_TIMESTAMP_MAX] = {}; +- _cleanup_free_ char *l = NULL; ++ _cleanup_free_ char *l = NULL, *username = NULL; + usec_t left; + int r; + +@@ -83,8 +83,8 @@ static int warn_wall(Manager *m, usec_t n) { + return 0; + } + +- utmp_wall(l, uid_to_name(m->scheduled_shutdown_uid), +- m->scheduled_shutdown_tty, logind_wall_tty_filter, m); ++ username = uid_to_name(m->scheduled_shutdown_uid); ++ utmp_wall(l, username, m->scheduled_shutdown_tty, logind_wall_tty_filter, m); + + return 1; + } diff --git a/SOURCES/0032-firewall-util-add-an-assert-that-we-re-not-overwriti.patch b/SOURCES/0032-firewall-util-add-an-assert-that-we-re-not-overwriti.patch new file mode 100644 index 0000000..e092826 --- /dev/null +++ b/SOURCES/0032-firewall-util-add-an-assert-that-we-re-not-overwriti.patch @@ -0,0 +1,36 @@ +From fbe394e9166ddfe847dcac0eab0fcbd3c225dc33 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Wed, 10 Oct 2018 09:33:28 +0200 +Subject: [PATCH] firewall-util: add an assert that we're not overwriting a + buffer + +... like commit f28501279d2c28fdbb31d8273b723e9bf71d3b98 does for +out_interface. + +(cherry picked from commit 0b777d20e9a3868b12372ffce8040d1be063cec7) + +Resolves: #1602706 +--- + src/shared/firewall-util.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c +index eb4f5ff616..cba52fb419 100644 +--- a/src/shared/firewall-util.c ++++ b/src/shared/firewall-util.c +@@ -50,8 +50,14 @@ static int entry_fill_basics( + entry->ip.proto = protocol; + + if (in_interface) { ++ size_t l; ++ ++ l = strlen(in_interface); ++ assert(l < sizeof entry->ip.iniface); ++ assert(l < sizeof entry->ip.iniface_mask); ++ + strcpy(entry->ip.iniface, in_interface); +- memset(entry->ip.iniface_mask, 0xFF, strlen(in_interface)+1); ++ memset(entry->ip.iniface_mask, 0xFF, l + 1); + } + if (source) { + entry->ip.src = source->in; diff --git a/SOURCES/0033-journal-file-avoid-calling-ftruncate-with-invalid-fd.patch b/SOURCES/0033-journal-file-avoid-calling-ftruncate-with-invalid-fd.patch new file mode 100644 index 0000000..f37d84e --- /dev/null +++ b/SOURCES/0033-journal-file-avoid-calling-ftruncate-with-invalid-fd.patch @@ -0,0 +1,29 @@ +From ebdb96247433d920b391672e019da9402aabd351 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Wed, 10 Oct 2018 13:56:54 +0200 +Subject: [PATCH] journal-file: avoid calling ftruncate with invalid fd + +This can happen if journal_file_close is called from the failure +handling code of journal_file_open before f->fd was established. + +(cherry picked from commit c52368509f48e556be5a4c7a171361b656a25e02) + +Resolves: #1602706 +--- + src/journal/journal-file.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c +index 62e7f68a13..efc3ee052b 100644 +--- a/src/journal/journal-file.c ++++ b/src/journal/journal-file.c +@@ -1846,6 +1846,9 @@ static int journal_file_append_entry_internal( + void journal_file_post_change(JournalFile *f) { + assert(f); + ++ if (f->fd < 0) ++ return; ++ + /* inotify() does not receive IN_MODIFY events from file + * accesses done via mmap(). After each access we hence + * trigger IN_MODIFY by truncating the journal file to its diff --git a/SOURCES/0034-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch b/SOURCES/0034-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch new file mode 100644 index 0000000..98a69f2 --- /dev/null +++ b/SOURCES/0034-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch @@ -0,0 +1,33 @@ +From c232bc1f346a6af9777c216d01f7940898ae1650 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 19 Oct 2018 12:12:33 +0200 +Subject: [PATCH] dhcp6: make sure we have enough space for the DHCP6 option + header + +Fixes a vulnerability originally discovered by Felix Wilhelm from +Google. + +CVE-2018-15688 +LP: #1795921 +https://bugzilla.redhat.com/show_bug.cgi?id=1639067 + +(cherry-picked from commit 4dac5eaba4e419b29c97da38a8b1f82336c2c892) + +Resolves: #1643363 +--- + src/libsystemd-network/dhcp6-option.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c +index 18196b1257..0979497299 100644 +--- a/src/libsystemd-network/dhcp6-option.c ++++ b/src/libsystemd-network/dhcp6-option.c +@@ -103,7 +103,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { + return -EINVAL; + } + +- if (*buflen < len) ++ if (*buflen < offsetof(DHCP6Option, data) + len) + return -ENOBUFS; + + ia_hdr = *buf; diff --git a/SOURCES/0035-core-rename-queued_message-pending_reload_message.patch b/SOURCES/0035-core-rename-queued_message-pending_reload_message.patch new file mode 100644 index 0000000..fa23d06 --- /dev/null +++ b/SOURCES/0035-core-rename-queued_message-pending_reload_message.patch @@ -0,0 +1,133 @@ +From 35a23324975ac6ee0bbd3408394f992007b7a439 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 13 Nov 2018 11:59:06 +0100 +Subject: [PATCH] =?UTF-8?q?core:=20rename=20queued=5Fmessage=20=E2=86=92?= + =?UTF-8?q?=20pending=5Freload=5Fmessage?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This field is only used for pending Reload() replies, hence let's rename +it to be more descriptive and precise. + +No change in behaviour. + +(cherry picked from commit 209de5256b7ba8600c3e73a85a43b86708998d65) + +Resolves: #1647359 +--- + src/core/dbus-manager.c | 4 ++-- + src/core/dbus.c | 12 ++++++------ + src/core/dbus.h | 2 +- + src/core/manager.c | 6 +++--- + src/core/manager.h | 2 +- + 5 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c +index 4ed68af1e0..d39c9b28c4 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -1329,8 +1329,8 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error * + * is finished. That way the caller knows when the reload + * finished. */ + +- assert(!m->queued_message); +- r = sd_bus_message_new_method_return(message, &m->queued_message); ++ assert(!m->pending_reload_message); ++ r = sd_bus_message_new_method_return(message, &m->pending_reload_message); + if (r < 0) + return r; + +diff --git a/src/core/dbus.c b/src/core/dbus.c +index bf5917696e..256a410215 100644 +--- a/src/core/dbus.c ++++ b/src/core/dbus.c +@@ -47,23 +47,23 @@ + + static void destroy_bus(Manager *m, sd_bus **bus); + +-int bus_send_queued_message(Manager *m) { ++int bus_send_pending_reload_message(Manager *m) { + int r; + + assert(m); + +- if (!m->queued_message) ++ if (!m->pending_reload_message) + return 0; + + /* If we cannot get rid of this message we won't dispatch any + * D-Bus messages, so that we won't end up wanting to queue + * another message. */ + +- r = sd_bus_send(NULL, m->queued_message, NULL); ++ r = sd_bus_send(NULL, m->pending_reload_message, NULL); + if (r < 0) + log_warning_errno(r, "Failed to send queued message: %m"); + +- m->queued_message = sd_bus_message_unref(m->queued_message); ++ m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message); + + return 0; + } +@@ -1079,8 +1079,8 @@ static void destroy_bus(Manager *m, sd_bus **bus) { + u->bus_track = sd_bus_track_unref(u->bus_track); + + /* Get rid of queued message on this bus */ +- if (m->queued_message && sd_bus_message_get_bus(m->queued_message) == *bus) +- m->queued_message = sd_bus_message_unref(m->queued_message); ++ if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus) ++ m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message); + + /* Possibly flush unwritten data, but only if we are + * unprivileged, since we don't want to sync here */ +diff --git a/src/core/dbus.h b/src/core/dbus.h +index 382a96da7d..f1c0fa86c0 100644 +--- a/src/core/dbus.h ++++ b/src/core/dbus.h +@@ -5,7 +5,7 @@ + + #include "manager.h" + +-int bus_send_queued_message(Manager *m); ++int bus_send_pending_reload_message(Manager *m); + + int bus_init_private(Manager *m); + int bus_init_api(Manager *m); +diff --git a/src/core/manager.c b/src/core/manager.c +index 930df4e23a..a24bfcacdf 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -2078,7 +2078,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { + return 0; + + /* Anything to do at all? */ +- if (!m->dbus_unit_queue && !m->dbus_job_queue && !m->send_reloading_done && !m->queued_message) ++ if (!m->dbus_unit_queue && !m->dbus_job_queue && !m->send_reloading_done && !m->pending_reload_message) + return 0; + + /* Do we have overly many messages queued at the moment? If so, let's not enqueue more on top, let's sit this +@@ -2123,8 +2123,8 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { + n++, budget--; + } + +- if (budget > 0 && m->queued_message) { +- bus_send_queued_message(m); ++ if (budget > 0 && m->pending_reload_message) { ++ bus_send_pending_reload_message(m); + n++; + } + +diff --git a/src/core/manager.h b/src/core/manager.h +index ea5d425030..c7f4d66ecd 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -215,7 +215,7 @@ struct Manager { + + /* This is used during reloading: before the reload we queue + * the reply message here, and afterwards we send it */ +- sd_bus_message *queued_message; ++ sd_bus_message *pending_reload_message; + + Hashmap *watch_bus; /* D-Bus names => Unit object n:1 */ + diff --git a/SOURCES/0036-core-when-we-can-t-send-the-pending-reload-message-s.patch b/SOURCES/0036-core-when-we-can-t-send-the-pending-reload-message-s.patch new file mode 100644 index 0000000..d75713d --- /dev/null +++ b/SOURCES/0036-core-when-we-can-t-send-the-pending-reload-message-s.patch @@ -0,0 +1,36 @@ +From 52a474cf15bf2b0edb449750eb63eb8cdb9a3780 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 13 Nov 2018 12:00:42 +0100 +Subject: [PATCH] core: when we can't send the pending reload message, say we + ignore it in the warning we log + +No change in behaviour, just better wording. + +(cherry picked from commit 4b66bccab004221b903b43b4c224442bfa3e9ac7) + +Resolves: #1647359 +--- + src/core/dbus.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/core/dbus.c b/src/core/dbus.c +index 256a410215..346a440c5d 100644 +--- a/src/core/dbus.c ++++ b/src/core/dbus.c +@@ -55,13 +55,12 @@ int bus_send_pending_reload_message(Manager *m) { + if (!m->pending_reload_message) + return 0; + +- /* If we cannot get rid of this message we won't dispatch any +- * D-Bus messages, so that we won't end up wanting to queue +- * another message. */ ++ /* If we cannot get rid of this message we won't dispatch any D-Bus messages, so that we won't end up wanting ++ * to queue another message. */ + + r = sd_bus_send(NULL, m->pending_reload_message, NULL); + if (r < 0) +- log_warning_errno(r, "Failed to send queued message: %m"); ++ log_warning_errno(r, "Failed to send queued message, ignoring: %m"); + + m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message); + diff --git a/SOURCES/0037-core-make-sure-we-don-t-throttle-change-signal-gener.patch b/SOURCES/0037-core-make-sure-we-don-t-throttle-change-signal-gener.patch new file mode 100644 index 0000000..603d269 --- /dev/null +++ b/SOURCES/0037-core-make-sure-we-don-t-throttle-change-signal-gener.patch @@ -0,0 +1,114 @@ +From 0412acb95ffac94d5916ee19991cc7194e55953c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 13 Nov 2018 12:48:49 +0100 +Subject: [PATCH] core: make sure we don't throttle change signal generator + when a reload is pending + +Fixes: #10627 +(cherry picked from commit b8d381c47776ea0440af175cbe0c02cb743bde08) + +Resolves: #1647359 +--- + src/core/manager.c | 64 ++++++++++++++++++++++++++++------------------ + 1 file changed, 39 insertions(+), 25 deletions(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index a24bfcacdf..3b2fe11e87 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -2074,56 +2074,70 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { + + assert(m); + ++ /* Avoid recursion */ + if (m->dispatching_dbus_queue) + return 0; + +- /* Anything to do at all? */ +- if (!m->dbus_unit_queue && !m->dbus_job_queue && !m->send_reloading_done && !m->pending_reload_message) +- return 0; ++ /* When we are reloading, let's not wait with generating signals, since we need to exit the manager as quickly ++ * as we can. There's no point in throttling generation of signals in that case. */ ++ if (MANAGER_IS_RELOADING(m) || m->send_reloading_done || m->pending_reload_message) ++ budget = (unsigned) -1; /* infinite budget in this case */ ++ else { ++ /* Anything to do at all? */ ++ if (!m->dbus_unit_queue && !m->dbus_job_queue) ++ return 0; + +- /* Do we have overly many messages queued at the moment? If so, let's not enqueue more on top, let's sit this +- * cycle out, and process things in a later cycle when the queues got a bit emptier. */ +- if (manager_bus_n_queued_write(m) > MANAGER_BUS_BUSY_THRESHOLD) +- return 0; ++ /* Do we have overly many messages queued at the moment? If so, let's not enqueue more on top, let's ++ * sit this cycle out, and process things in a later cycle when the queues got a bit emptier. */ ++ if (manager_bus_n_queued_write(m) > MANAGER_BUS_BUSY_THRESHOLD) ++ return 0; + +- /* Only process a certain number of units/jobs per event loop iteration. Even if the bus queue wasn't overly +- * full before this call we shouldn't increase it in size too wildly in one step, and we shouldn't monopolize +- * CPU time with generating these messages. Note the difference in counting of this "budget" and the +- * "threshold" above: the "budget" is decreased only once per generated message, regardless how many +- * busses/direct connections it is enqueued on, while the "threshold" is applied to each queued instance of bus +- * message, i.e. if the same message is enqueued to five busses/direct connections it will be counted five +- * times. This difference in counting ("references" vs. "instances") is primarily a result of the fact that +- * it's easier to implement it this way, however it also reflects the thinking that the "threshold" should put +- * a limit on used queue memory, i.e. space, while the "budget" should put a limit on time. Also note that +- * the "threshold" is currently chosen much higher than the "budget". */ +- budget = MANAGER_BUS_MESSAGE_BUDGET; ++ /* Only process a certain number of units/jobs per event loop iteration. Even if the bus queue wasn't ++ * overly full before this call we shouldn't increase it in size too wildly in one step, and we ++ * shouldn't monopolize CPU time with generating these messages. Note the difference in counting of ++ * this "budget" and the "threshold" above: the "budget" is decreased only once per generated message, ++ * regardless how many busses/direct connections it is enqueued on, while the "threshold" is applied to ++ * each queued instance of bus message, i.e. if the same message is enqueued to five busses/direct ++ * connections it will be counted five times. This difference in counting ("references" ++ * vs. "instances") is primarily a result of the fact that it's easier to implement it this way, ++ * however it also reflects the thinking that the "threshold" should put a limit on used queue memory, ++ * i.e. space, while the "budget" should put a limit on time. Also note that the "threshold" is ++ * currently chosen much higher than the "budget". */ ++ budget = MANAGER_BUS_MESSAGE_BUDGET; ++ } + + m->dispatching_dbus_queue = true; + +- while (budget > 0 && (u = m->dbus_unit_queue)) { ++ while (budget != 0 && (u = m->dbus_unit_queue)) { + + assert(u->in_dbus_queue); + + bus_unit_send_change_signal(u); +- n++, budget--; ++ n++; ++ ++ if (budget != (unsigned) -1) ++ budget--; + } + +- while (budget > 0 && (j = m->dbus_job_queue)) { ++ while (budget != 0 && (j = m->dbus_job_queue)) { + assert(j->in_dbus_queue); + + bus_job_send_change_signal(j); +- n++, budget--; ++ n++; ++ ++ if (budget != (unsigned) -1) ++ budget--; + } + + m->dispatching_dbus_queue = false; + +- if (budget > 0 && m->send_reloading_done) { ++ if (m->send_reloading_done) { + m->send_reloading_done = false; + bus_manager_send_reloading(m, false); +- n++, budget--; ++ n++; + } + +- if (budget > 0 && m->pending_reload_message) { ++ if (m->pending_reload_message) { + bus_send_pending_reload_message(m); + n++; + } diff --git a/SOURCES/0038-proc-cmdline-introduce-PROC_CMDLINE_RD_STRICT.patch b/SOURCES/0038-proc-cmdline-introduce-PROC_CMDLINE_RD_STRICT.patch new file mode 100644 index 0000000..c1be01e --- /dev/null +++ b/SOURCES/0038-proc-cmdline-introduce-PROC_CMDLINE_RD_STRICT.patch @@ -0,0 +1,45 @@ +From 84b15a8a493424efa8c9eaa9a44a23c3c59742bd Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Thu, 25 Oct 2018 16:21:26 +0200 +Subject: [PATCH] proc-cmdline: introduce PROC_CMDLINE_RD_STRICT + +Our current set of flags allows an option to be either +use just in initrd or both in initrd and normal system. +This new flag is intended to be used in the case where +you want apply some settings just in initrd or just +in normal system. + +(cherry picked from commit ed58820d7669971762dd887dc117d922c23f2543) + +Related: #1643429 +--- + src/basic/proc-cmdline.c | 3 ++- + src/basic/proc-cmdline.h | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c +index add481c2ae..530ac37460 100644 +--- a/src/basic/proc-cmdline.c ++++ b/src/basic/proc-cmdline.c +@@ -72,7 +72,8 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned fla + + if (flags & PROC_CMDLINE_STRIP_RD_PREFIX) + key = q; +- } ++ } else if (in_initrd() && flags & PROC_CMDLINE_RD_STRICT) ++ continue; + + value = strchr(key, '='); + if (value) +diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h +index 4a9e6e0f62..140200dbf4 100644 +--- a/src/basic/proc-cmdline.h ++++ b/src/basic/proc-cmdline.h +@@ -8,6 +8,7 @@ + enum { + PROC_CMDLINE_STRIP_RD_PREFIX = 1, + PROC_CMDLINE_VALUE_OPTIONAL = 2, ++ PROC_CMDLINE_RD_STRICT = 4 + }; + + typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data); diff --git a/SOURCES/0039-debug-generator-introduce-rd.-version-of-all-options.patch b/SOURCES/0039-debug-generator-introduce-rd.-version-of-all-options.patch new file mode 100644 index 0000000..ac7a1b1 --- /dev/null +++ b/SOURCES/0039-debug-generator-introduce-rd.-version-of-all-options.patch @@ -0,0 +1,77 @@ +From 55798355455b9255458d6a705f8766c4dbe3ef73 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Thu, 25 Oct 2018 16:34:00 +0200 +Subject: [PATCH] debug-generator: introduce rd.* version of all options + +(cherry picked from commit a7dd6d04b07f58df5c0294743d76df0be0b4b928) + +Resolves: #1643429 +--- + man/systemd-debug-generator.xml | 27 +++++++++++++++++++-------- + src/debug-generator/debug-generator.c | 2 +- + 2 files changed, 20 insertions(+), 9 deletions(-) + +diff --git a/man/systemd-debug-generator.xml b/man/systemd-debug-generator.xml +index d5cf4109b0..fa88e8ac01 100644 +--- a/man/systemd-debug-generator.xml ++++ b/man/systemd-debug-generator.xml +@@ -33,27 +33,38 @@ + that reads the kernel command line and understands three + options: + +- If the option is specified +- and followed by a unit name, this unit is masked for the runtime, +- similar to the effect of ++ If the or ++ option is specified and followed by a unit name, this unit is ++ masked for the runtime, similar to the effect of + systemctl1's + mask command. This is useful to boot with + certain units removed from the initial boot transaction for +- debugging system startup. May be specified more than once. ++ debugging system startup. May be specified more than once. ++ is honored only by initial ++ RAM disk (initrd) while is ++ honored only in the main system. + +- If the option is specified ++ If the or ++ option is specified + and followed by a unit name, a start job for this unit is added to + the initial transaction. This is useful to start one or more +- additional units at boot. May be specified more than once. ++ additional units at boot. May be specified more than once. ++ is honored only by initial ++ RAM disk (initrd) while is ++ honored only in the main system. + +- If the option is ++ If the or ++ option is + specified, the debug shell service + debug-shell.service is pulled into the boot + transaction. It will spawn a debug shell on tty9 during early + system startup. Note that the shell may also be turned on + persistently by enabling it with + systemctl1's +- enable command. ++ enable command. ++ is honored only by initial ++ RAM disk (initrd) while is ++ honored only in the main system. + + systemd-debug-generator implements + systemd.generator7. +diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c +index dd6ab94fa2..800d31cebe 100644 +--- a/src/debug-generator/debug-generator.c ++++ b/src/debug-generator/debug-generator.c +@@ -154,7 +154,7 @@ int main(int argc, char *argv[]) { + + umask(0022); + +- r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); ++ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_RD_STRICT | PROC_CMDLINE_STRIP_RD_PREFIX); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + diff --git a/SOURCES/0040-chown-recursive-let-s-rework-the-recursive-logic-to-.patch b/SOURCES/0040-chown-recursive-let-s-rework-the-recursive-logic-to-.patch new file mode 100644 index 0000000..c27e8aa --- /dev/null +++ b/SOURCES/0040-chown-recursive-let-s-rework-the-recursive-logic-to-.patch @@ -0,0 +1,213 @@ +From 107d75ca9394481bd045385fc45f2ee65b30ad16 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 19 Oct 2018 11:26:59 +0200 +Subject: [PATCH] chown-recursive: let's rework the recursive logic to use + O_PATH + +That way we can pin a specific inode and analyze it and manipulate it +without it being swapped out beneath our hands. + +Fixes a vulnerability originally found by Jann Horn from Google. + +CVE-2018-15687 +LP: #1796692 +https://bugzilla.redhat.com/show_bug.cgi?id=1639076 + +(cherry-picked from commit 5de6cce58b3e8b79239b6e83653459d91af6e57c) + +Resolves: #1643368 +--- + src/core/chown-recursive.c | 146 ++++++++++++++++++------------------- + 1 file changed, 70 insertions(+), 76 deletions(-) + +diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c +index c4794501c2..27c64489b5 100644 +--- a/src/core/chown-recursive.c ++++ b/src/core/chown-recursive.c +@@ -1,17 +1,19 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + +-#include +-#include + #include ++#include ++#include + +-#include "user-util.h" +-#include "macro.h" +-#include "fd-util.h" +-#include "dirent-util.h" + #include "chown-recursive.h" ++#include "dirent-util.h" ++#include "fd-util.h" ++#include "macro.h" ++#include "stdio-util.h" ++#include "strv.h" ++#include "user-util.h" + +-static int chown_one(int fd, const char *name, const struct stat *st, uid_t uid, gid_t gid) { +- int r; ++static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) { ++ char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + + assert(fd >= 0); + assert(st); +@@ -20,90 +22,82 @@ static int chown_one(int fd, const char *name, const struct stat *st, uid_t uid, + (!gid_is_valid(gid) || st->st_gid == gid)) + return 0; + +- if (name) +- r = fchownat(fd, name, uid, gid, AT_SYMLINK_NOFOLLOW); +- else +- r = fchown(fd, uid, gid); +- if (r < 0) +- return -errno; ++ /* We change ownership through the /proc/self/fd/%i path, so that we have a stable reference that works with ++ * O_PATH. (Note: fchown() and fchmod() do not work with O_PATH, the kernel refuses that. */ ++ xsprintf(procfs_path, "/proc/self/fd/%i", fd); + +- /* The linux kernel alters the mode in some cases of chown(). Let's undo this. */ +- if (name) { +- if (!S_ISLNK(st->st_mode)) +- r = fchmodat(fd, name, st->st_mode, 0); +- else /* There's currently no AT_SYMLINK_NOFOLLOW for fchmodat() */ +- r = 0; +- } else +- r = fchmod(fd, st->st_mode); +- if (r < 0) ++ if (chown(procfs_path, uid, gid) < 0) + return -errno; + ++ /* The linux kernel alters the mode in some cases of chown(). Let's undo this. We do this only for non-symlinks ++ * however. That's because for symlinks the access mode is ignored anyway and because on some kernels/file ++ * systems trying to change the access mode will succeed but has no effect while on others it actively ++ * fails. */ ++ if (!S_ISLNK(st->st_mode)) ++ if (chmod(procfs_path, st->st_mode & 07777) < 0) ++ return -errno; ++ + return 1; + } + + static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gid_t gid) { ++ _cleanup_closedir_ DIR *d = NULL; + bool changed = false; ++ struct dirent *de; + int r; + + assert(fd >= 0); + assert(st); + +- if (S_ISDIR(st->st_mode)) { +- _cleanup_closedir_ DIR *d = NULL; +- struct dirent *de; +- +- d = fdopendir(fd); +- if (!d) { +- r = -errno; +- goto finish; +- } +- fd = -1; +- +- FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { +- struct stat fst; +- +- if (dot_or_dot_dot(de->d_name)) +- continue; +- +- if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) { +- r = -errno; +- goto finish; +- } +- +- if (S_ISDIR(fst.st_mode)) { +- int subdir_fd; +- +- subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); +- if (subdir_fd < 0) { +- r = -errno; +- goto finish; +- } +- +- r = chown_recursive_internal(subdir_fd, &fst, uid, gid); +- if (r < 0) +- goto finish; +- if (r > 0) +- changed = true; +- } else { +- r = chown_one(dirfd(d), de->d_name, &fst, uid, gid); +- if (r < 0) +- goto finish; +- if (r > 0) +- changed = true; +- } ++ d = fdopendir(fd); ++ if (!d) { ++ safe_close(fd); ++ return -errno; ++ } ++ ++ FOREACH_DIRENT_ALL(de, d, return -errno) { ++ _cleanup_close_ int path_fd = -1; ++ struct stat fst; ++ ++ if (dot_or_dot_dot(de->d_name)) ++ continue; ++ ++ /* Let's pin the child inode we want to fix now with an O_PATH fd, so that it cannot be swapped out ++ * while we manipulate it. */ ++ path_fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW); ++ if (path_fd < 0) ++ return -errno; ++ ++ if (fstat(path_fd, &fst) < 0) ++ return -errno; ++ ++ if (S_ISDIR(fst.st_mode)) { ++ int subdir_fd; ++ ++ /* Convert it to a "real" (i.e. non-O_PATH) fd now */ ++ subdir_fd = fd_reopen(path_fd, O_RDONLY|O_CLOEXEC|O_NOATIME); ++ if (subdir_fd < 0) ++ return subdir_fd; ++ ++ r = chown_recursive_internal(subdir_fd, &fst, uid, gid); /* takes possession of subdir_fd even on failure */ ++ if (r < 0) ++ return r; ++ if (r > 0) ++ changed = true; ++ } else { ++ r = chown_one(path_fd, &fst, uid, gid); ++ if (r < 0) ++ return r; ++ if (r > 0) ++ changed = true; + } ++ } + +- r = chown_one(dirfd(d), NULL, st, uid, gid); +- } else +- r = chown_one(fd, NULL, st, uid, gid); ++ r = chown_one(dirfd(d), st, uid, gid); + if (r < 0) +- goto finish; ++ return r; + +- r = r > 0 || changed; +- +-finish: +- safe_close(fd); +- return r; ++ return r > 0 || changed; + } + + int path_chown_recursive(const char *path, uid_t uid, gid_t gid) { +@@ -111,7 +105,7 @@ int path_chown_recursive(const char *path, uid_t uid, gid_t gid) { + struct stat st; + int r; + +- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); ++ fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) + return -errno; + diff --git a/SOURCES/0041-chown-recursive-also-drop-ACLs-when-recursively-chow.patch b/SOURCES/0041-chown-recursive-also-drop-ACLs-when-recursively-chow.patch new file mode 100644 index 0000000..edf2368 --- /dev/null +++ b/SOURCES/0041-chown-recursive-also-drop-ACLs-when-recursively-chow.patch @@ -0,0 +1,58 @@ +From bbe9ac11d8d4a8511214605509a593fb9f04ffaa Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 19 Oct 2018 11:28:40 +0200 +Subject: [PATCH] chown-recursive: also drop ACLs when recursively chown()ing + +Let's better be safe than sorry and also drop ACLs. + +(cherry-picked from commit f89bc84f3242449cbc308892c87573b131f121df) + +Related: #1643368 +--- + src/core/chown-recursive.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c +index 27c64489b5..447b771267 100644 +--- a/src/core/chown-recursive.c ++++ b/src/core/chown-recursive.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #include "chown-recursive.h" + #include "dirent-util.h" +@@ -14,6 +15,7 @@ + + static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; ++ const char *n; + + assert(fd >= 0); + assert(st); +@@ -26,13 +28,19 @@ static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) { + * O_PATH. (Note: fchown() and fchmod() do not work with O_PATH, the kernel refuses that. */ + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + ++ /* Drop any ACL if there is one */ ++ FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default") ++ if (removexattr(procfs_path, n) < 0) ++ if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY)) ++ return -errno; ++ + if (chown(procfs_path, uid, gid) < 0) + return -errno; + +- /* The linux kernel alters the mode in some cases of chown(). Let's undo this. We do this only for non-symlinks +- * however. That's because for symlinks the access mode is ignored anyway and because on some kernels/file +- * systems trying to change the access mode will succeed but has no effect while on others it actively +- * fails. */ ++ /* The linux kernel alters the mode in some cases of chown(), as well when we change ACLs. Let's undo this. We ++ * do this only for non-symlinks however. That's because for symlinks the access mode is ignored anyway and ++ * because on some kernels/file systems trying to change the access mode will succeed but has no effect while ++ * on others it actively fails. */ + if (!S_ISLNK(st->st_mode)) + if (chmod(procfs_path, st->st_mode & 07777) < 0) + return -errno; diff --git a/SOURCES/0042-chown-recursive-TAKE_FD-is-your-friend.patch b/SOURCES/0042-chown-recursive-TAKE_FD-is-your-friend.patch new file mode 100644 index 0000000..541b9a2 --- /dev/null +++ b/SOURCES/0042-chown-recursive-TAKE_FD-is-your-friend.patch @@ -0,0 +1,34 @@ +From c9630164b869e109bf2960968fc583449ccf0875 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 19 Oct 2018 11:42:11 +0200 +Subject: [PATCH] chown-recursive: TAKE_FD() is your friend + +(cherry-picked from commit cd6b7d50c337b3676a3d5fc2188ff298dcbdb939) + +Related: #1643368 +--- + src/core/chown-recursive.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c +index 447b771267..7767301f7d 100644 +--- a/src/core/chown-recursive.c ++++ b/src/core/chown-recursive.c +@@ -111,7 +111,6 @@ static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gi + int path_chown_recursive(const char *path, uid_t uid, gid_t gid) { + _cleanup_close_ int fd = -1; + struct stat st; +- int r; + + fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) +@@ -130,8 +129,5 @@ int path_chown_recursive(const char *path, uid_t uid, gid_t gid) { + (!gid_is_valid(gid) || st.st_gid == gid)) + return 0; + +- r = chown_recursive_internal(fd, &st, uid, gid); +- fd = -1; /* we donated the fd to the call, regardless if it succeeded or failed */ +- +- return r; ++ return chown_recursive_internal(TAKE_FD(fd), &st, uid, gid); /* we donate the fd to the call, regardless if it succeeded or failed */ + } diff --git a/SOURCES/0043-test-add-test-case-for-recursive-chown-ing.patch b/SOURCES/0043-test-add-test-case-for-recursive-chown-ing.patch new file mode 100644 index 0000000..7a77368 --- /dev/null +++ b/SOURCES/0043-test-add-test-case-for-recursive-chown-ing.patch @@ -0,0 +1,200 @@ +From b53f89d56a5b7528735ddf335f8b47ab3e1a947a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 19 Oct 2018 11:31:37 +0200 +Subject: [PATCH] test: add test case for recursive chown()ing + +[msekleta: I removed call to log_test_skipped() and replaced it with older construct log_info() + return EXIT_TEST_SKIP] + +(cherry-picked from commit cb9e44db36caefcbb8ee7a12e14217305ed69ff2) + +Related: #1643368 +--- + src/test/meson.build | 5 ++ + src/test/test-chown-rec.c | 162 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 167 insertions(+) + create mode 100644 src/test/test-chown-rec.c + +diff --git a/src/test/meson.build b/src/test/meson.build +index 7da7e3a22c..b982251b1f 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -60,6 +60,11 @@ tests += [ + libmount, + libblkid]], + ++ [['src/test/test-chown-rec.c'], ++ [libcore, ++ libshared], ++ []], ++ + [['src/test/test-job-type.c'], + [libcore, + libshared], +diff --git a/src/test/test-chown-rec.c b/src/test/test-chown-rec.c +new file mode 100644 +index 0000000000..f16d4d4ba2 +--- /dev/null ++++ b/src/test/test-chown-rec.c +@@ -0,0 +1,162 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++ ++#include "alloc-util.h" ++#include "chown-recursive.h" ++#include "fileio.h" ++#include "log.h" ++#include "rm-rf.h" ++#include "string-util.h" ++#include "tests.h" ++ ++static const uint8_t acl[] = { ++ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, ++ 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x07, 0x00, ++ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00, ++ 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x07, 0x00, ++ 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x05, 0x00, ++ 0xff, 0xff, 0xff, 0xff, ++}; ++ ++static const uint8_t default_acl[] = { ++ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, ++ 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x07, 0x00, ++ 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x07, 0x00, ++ 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x07, 0x00, ++ 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x05, 0x00, ++ 0xff, 0xff, 0xff, 0xff, ++}; ++ ++static bool has_xattr(const char *p) { ++ char buffer[sizeof(acl) * 4]; ++ ++ if (lgetxattr(p, "system.posix_acl_access", buffer, sizeof(buffer)) < 0) { ++ if (IN_SET(errno, EOPNOTSUPP, ENOTTY, ENODATA, ENOSYS)) ++ return false; ++ } ++ ++ return true; ++} ++ ++static void test_chown_recursive(void) { ++ _cleanup_(rm_rf_physical_and_freep) char *t = NULL; ++ struct stat st; ++ const char *p; ++ ++ umask(022); ++ assert_se(mkdtemp_malloc(NULL, &t) >= 0); ++ ++ p = strjoina(t, "/dir"); ++ assert_se(mkdir(p, 0777) >= 0); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISDIR(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 0); ++ assert_se(st.st_gid == 0); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/symlink"); ++ assert_se(symlink("../../", p) >= 0); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISLNK(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0777); ++ assert_se(st.st_uid == 0); ++ assert_se(st.st_gid == 0); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/reg"); ++ assert_se(mknod(p, S_IFREG|0777, 0) >= 0); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISREG(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 0); ++ assert_se(st.st_gid == 0); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/sock"); ++ assert_se(mknod(p, S_IFSOCK|0777, 0) >= 0); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISSOCK(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 0); ++ assert_se(st.st_gid == 0); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/fifo"); ++ assert_se(mknod(p, S_IFIFO|0777, 0) >= 0); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISFIFO(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 0); ++ assert_se(st.st_gid == 0); ++ assert_se(!has_xattr(p)); ++ ++ /* We now apply an xattr to the dir, and check it again */ ++ p = strjoina(t, "/dir"); ++ assert_se(setxattr(p, "system.posix_acl_access", acl, sizeof(acl), 0) >= 0); ++ assert_se(setxattr(p, "system.posix_acl_default", default_acl, sizeof(default_acl), 0) >= 0); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISDIR(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0775); /* acl change changed the mode too */ ++ assert_se(st.st_uid == 0); ++ assert_se(st.st_gid == 0); ++ assert_se(has_xattr(p)); ++ ++ assert_se(path_chown_recursive(t, 1, 2) >= 0); ++ ++ p = strjoina(t, "/dir"); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISDIR(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0775); ++ assert_se(st.st_uid == 1); ++ assert_se(st.st_gid == 2); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/symlink"); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISLNK(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0777); ++ assert_se(st.st_uid == 1); ++ assert_se(st.st_gid == 2); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/reg"); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISREG(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 1); ++ assert_se(st.st_gid == 2); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/sock"); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISSOCK(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 1); ++ assert_se(st.st_gid == 2); ++ assert_se(!has_xattr(p)); ++ ++ p = strjoina(t, "/dir/fifo"); ++ assert_se(lstat(p, &st) >= 0); ++ assert_se(S_ISFIFO(st.st_mode)); ++ assert_se((st.st_mode & 07777) == 0755); ++ assert_se(st.st_uid == 1); ++ assert_se(st.st_gid == 2); ++ assert_se(!has_xattr(p)); ++} ++ ++int main(int argc, char *argv[]) { ++ log_set_max_level(LOG_DEBUG); ++ log_parse_environment(); ++ log_open(); ++ ++ if (geteuid() != 0) { ++ log_info("not running as root"); ++ return EXIT_TEST_SKIP; ++ } ++ ++ test_chown_recursive(); ++ ++ return EXIT_SUCCESS; ++} diff --git a/SOURCES/0044-Revert-sysctl.d-request-ECN-on-both-in-and-outgoing-.patch b/SOURCES/0044-Revert-sysctl.d-request-ECN-on-both-in-and-outgoing-.patch new file mode 100644 index 0000000..bdb582c --- /dev/null +++ b/SOURCES/0044-Revert-sysctl.d-request-ECN-on-both-in-and-outgoing-.patch @@ -0,0 +1,32 @@ +From 730ce6562f8a5f4a61d1ed3ffb4d65fa27b728fc Mon Sep 17 00:00:00 2001 +From: Thomas Hindoe Paaboel Andersen +Date: Fri, 17 Aug 2018 21:31:05 +0200 +Subject: [PATCH] Revert "sysctl.d: request ECN on both in and outgoing + connections" + +Turning on ECN still causes slow or broken network on linux. Our tcp +is not yet ready for wide spread use of ECN. + +This reverts commit 919472741dba6ad0a3f6c2b76d390a02d0e2fdc3. + +(cherry picked from commit 1e190dfd5bb95036f937ef1dc46f43eb0a146612) + +Resolves: #1619790 +--- + sysctl.d/50-default.conf | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/sysctl.d/50-default.conf b/sysctl.d/50-default.conf +index b67ae87ca6..e263cf0628 100644 +--- a/sysctl.d/50-default.conf ++++ b/sysctl.d/50-default.conf +@@ -33,9 +33,6 @@ net.ipv4.conf.all.promote_secondaries = 1 + # Fair Queue CoDel packet scheduler to fight bufferbloat + net.core.default_qdisc = fq_codel + +-# Request Explicit Congestion Notification (ECN) on both in and outgoing connections +-net.ipv4.tcp_ecn = 1 +- + # Enable hard and soft link protection + fs.protected_hardlinks = 1 + fs.protected_symlinks = 1 diff --git a/SOURCES/0045-detect-virt-do-not-try-to-read-all-of-proc-cpuinfo.patch b/SOURCES/0045-detect-virt-do-not-try-to-read-all-of-proc-cpuinfo.patch new file mode 100644 index 0000000..9669b1a --- /dev/null +++ b/SOURCES/0045-detect-virt-do-not-try-to-read-all-of-proc-cpuinfo.patch @@ -0,0 +1,84 @@ +From 886e5b028953404f2d924b561c0689d3e50dbbf4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 13 Sep 2018 09:24:36 +0200 +Subject: [PATCH] detect-virt: do not try to read all of /proc/cpuinfo + +Quoting https://github.com/systemd/systemd/issues/10074: +> detect_vm_uml() reads /proc/cpuinfo with read_full_file() +> read_full_file() has a file max limit size of READ_FULL_BYTES_MAX=(4U*1024U*1024U) +> Unfortunately, the size of my /proc/cpuinfo is bigger, approximately: +> echo $(( 4* $(cat /proc/cpuinfo | wc -c))) +> 9918072 +> This causes read_full_file() to fail and the Condition test fallout. + +Let's just read line by line until we find an intersting line. This also +helps if not running under UML, because we avoid reading as much data. + +(cherry picked from commit 6058516a14ada1748313af6783f5b4e7e3006654) + +Resolves: #1631532 +--- + src/basic/virt.c | 38 ++++++++++++++++++++++++++++---------- + 1 file changed, 28 insertions(+), 10 deletions(-) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index d347732bb3..e05b3e6d99 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -11,6 +11,7 @@ + + #include "alloc-util.h" + #include "dirent-util.h" ++#include "def.h" + #include "env-util.h" + #include "fd-util.h" + #include "fileio.h" +@@ -259,21 +260,38 @@ static int detect_vm_hypervisor(void) { + } + + static int detect_vm_uml(void) { +- _cleanup_free_ char *cpuinfo_contents = NULL; ++ _cleanup_fclose_ FILE *f = NULL; + int r; + + /* Detect User-Mode Linux by reading /proc/cpuinfo */ +- r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); +- if (r == -ENOENT) { +- log_debug("/proc/cpuinfo not found, assuming no UML virtualization."); +- return VIRTUALIZATION_NONE; ++ f = fopen("/proc/cpuinfo", "re"); ++ if (!f) { ++ if (errno == ENOENT) { ++ log_debug("/proc/cpuinfo not found, assuming no UML virtualization."); ++ return VIRTUALIZATION_NONE; ++ } ++ return -errno; + } +- if (r < 0) +- return r; + +- if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { +- log_debug("UML virtualization found in /proc/cpuinfo"); +- return VIRTUALIZATION_UML; ++ for (;;) { ++ _cleanup_free_ char *line = NULL; ++ const char *t; ++ ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ t = startswith(line, "vendor_id\t: "); ++ if (t) { ++ if (startswith(t, "User Mode Linux")) { ++ log_debug("UML virtualization found in /proc/cpuinfo"); ++ return VIRTUALIZATION_UML; ++ } ++ ++ break; ++ } + } + + log_debug("UML virtualization not found in /proc/cpuinfo."); diff --git a/SOURCES/0046-sd-bus-unify-three-code-paths-which-free-struct-bus_.patch b/SOURCES/0046-sd-bus-unify-three-code-paths-which-free-struct-bus_.patch new file mode 100644 index 0000000..920053e --- /dev/null +++ b/SOURCES/0046-sd-bus-unify-three-code-paths-which-free-struct-bus_.patch @@ -0,0 +1,166 @@ +From eb141ba81158feb74118da4e7a3f2266b11ffe10 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 9 Jul 2018 08:06:28 +0200 +Subject: [PATCH] sd-bus: unify three code-paths which free struct + bus_container + +We didn't free one of the fields in two of the places. + +$ valgrind --show-leak-kinds=all --leak-check=full \ + build/fuzz-bus-message \ + test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20 +... +==14457== HEAP SUMMARY: +==14457== in use at exit: 3 bytes in 1 blocks +==14457== total heap usage: 509 allocs, 508 frees, 51,016 bytes allocated +==14457== +==14457== 3 bytes in 1 blocks are definitely lost in loss record 1 of 1 +==14457== at 0x4C2EBAB: malloc (vg_replace_malloc.c:299) +==14457== by 0x53AFE79: strndup (in /usr/lib64/libc-2.27.so) +==14457== by 0x4F52EB8: free_and_strndup (string-util.c:1039) +==14457== by 0x4F8E1AB: sd_bus_message_peek_type (bus-message.c:4193) +==14457== by 0x4F76CB5: bus_message_dump (bus-dump.c:144) +==14457== by 0x108F12: LLVMFuzzerTestOneInput (fuzz-bus-message.c:24) +==14457== by 0x1090F7: main (fuzz-main.c:34) +==14457== +==14457== LEAK SUMMARY: +==14457== definitely lost: 3 bytes in 1 blocks + +(cherry picked from commit 6d1e0f4fcba8d6f425da3dc91805db95399b3c8b) +Resolves: #1635435 +--- + src/libsystemd/sd-bus/bus-message.c | 64 +++++++++--------- + ...k-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20 | Bin 0 -> 534 bytes + 2 files changed, 32 insertions(+), 32 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20 + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 7c8bad2bdd..d55cb14843 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -77,19 +77,38 @@ static void message_reset_parts(sd_bus_message *m) { + m->cached_rindex_part_begin = 0; + } + +-static void message_reset_containers(sd_bus_message *m) { +- unsigned i; ++static struct bus_container *message_get_container(sd_bus_message *m) { ++ assert(m); ++ ++ if (m->n_containers == 0) ++ return &m->root_container; ++ ++ assert(m->containers); ++ return m->containers + m->n_containers - 1; ++} ++ ++static void message_free_last_container(sd_bus_message *m) { ++ struct bus_container *c; ++ ++ c = message_get_container(m); ++ ++ free(c->signature); ++ free(c->peeked_signature); ++ free(c->offsets); ++ ++ /* Move to previous container, but not if we are on root container */ ++ if (m->n_containers > 0) ++ m->n_containers--; ++} + ++static void message_reset_containers(sd_bus_message *m) { + assert(m); + +- for (i = 0; i < m->n_containers; i++) { +- free(m->containers[i].signature); +- free(m->containers[i].offsets); +- } ++ while (m->n_containers > 0) ++ message_free_last_container(m); + + m->containers = mfree(m->containers); +- +- m->n_containers = m->containers_allocated = 0; ++ m->containers_allocated = 0; + m->root_container.index = 0; + } + +@@ -112,10 +131,8 @@ static sd_bus_message* message_free(sd_bus_message *m) { + free(m->iovec); + + message_reset_containers(m); +- free(m->root_container.signature); +- free(m->root_container.offsets); +- +- free(m->root_container.peeked_signature); ++ assert(m->n_containers == 0); ++ message_free_last_container(m); + + bus_creds_done(&m->creds); + return mfree(m); +@@ -1113,16 +1130,6 @@ _public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message * + return 0; + } + +-static struct bus_container *message_get_container(sd_bus_message *m) { +- assert(m); +- +- if (m->n_containers == 0) +- return &m->root_container; +- +- assert(m->containers); +- return m->containers + m->n_containers - 1; +-} +- + struct bus_body_part *message_append_part(sd_bus_message *m) { + struct bus_body_part *part; + +@@ -4108,13 +4115,9 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) { + return -EBUSY; + } + +- free(c->signature); +- free(c->peeked_signature); +- free(c->offsets); +- m->n_containers--; ++ message_free_last_container(m); + + c = message_get_container(m); +- + saved = c->index; + c->index = c->saved_index; + r = container_next_item(m, c, &m->rindex); +@@ -4132,16 +4135,13 @@ static void message_quit_container(sd_bus_message *m) { + assert(m->sealed); + assert(m->n_containers > 0); + +- c = message_get_container(m); +- + /* Undo seeks */ ++ c = message_get_container(m); + assert(m->rindex >= c->before); + m->rindex = c->before; + + /* Free container */ +- free(c->signature); +- free(c->offsets); +- m->n_containers--; ++ message_free_last_container(m); + + /* Correct index of new top-level container */ + c = message_get_container(m); +diff --git a/test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20 b/test/fuzz/fuzz-bus-message/leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20 +new file mode 100644 +index 0000000000000000000000000000000000000000..c371824ffb604708619fd0713e8fca609bac18f7 +GIT binary patch +literal 534 +zcmZ{h!A`?442GSJP20o?A&zJgm*%pT#&`l!4rxq{&>8YmwQrOs;B(}I_m11m8`nFp`#ek1>oQYVSs`!XH?7Y=}3y9Ye+UliL9^x9s66$8wH+TPdOG`n| +z5Uhx +Date: Wed, 31 Oct 2018 12:50:19 +0100 +Subject: [PATCH] sd-bus: properly initialize containers + +Fixes a SIGSEGV introduced by commit 38a5315a3a6fab745d8c86ff9e486faaf50b28d1. +The same problem doesn't exist upstream, as the container structure +there is initialized using a compound literal, which is zeroed out by +default. + +Related: #1635435 +--- + src/libsystemd/sd-bus/bus-message.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index d55cb14843..780c8c6185 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -2004,6 +2004,7 @@ _public_ int sd_bus_message_open_container( + w = m->containers + m->n_containers++; + w->enclosing = type; + w->signature = TAKE_PTR(signature); ++ w->peeked_signature = NULL; + w->index = 0; + w->array_size = array_size; + w->before = before; diff --git a/SOURCES/0048-cryptsetup-generator-introduce-basic-keydev-support.patch b/SOURCES/0048-cryptsetup-generator-introduce-basic-keydev-support.patch new file mode 100644 index 0000000..d95e2f5 --- /dev/null +++ b/SOURCES/0048-cryptsetup-generator-introduce-basic-keydev-support.patch @@ -0,0 +1,240 @@ +From 0977e6b34fb5f28fc94f1df32261742881fa9bbe Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 30 Aug 2018 08:45:11 +0000 +Subject: [PATCH] cryptsetup-generator: introduce basic keydev support + +Dracut has a support for unlocking encrypted drives with keyfile stored +on the external drive. This support is included in the generated initrd +only if systemd module is not included. + +When systemd is used in initrd then attachment of encrypted drives is +handled by systemd-cryptsetup tools. Our generator has support for +keyfile, however, it didn't support keyfile on the external block +device (keydev). + +This commit introduces basic keydev support. Keydev can be specified per +luks.uuid on the kernel command line. Keydev is automatically mounted +during boot and we look for keyfile in the keydev +mountpoint (i.e. keyfile path is prefixed with the keydev mount point +path). After crypt device is attached we automatically unmount +where keyfile resides. + +Example: + rd.luks.key=70bc876b-f627-4038-9049-3080d79d2165=/key:LABEL=KEYDEV + +(cherry-picked from commit 70f5f48eb891b12e969577b464de61e15a2593da) + +Resolves: #1656869 +--- + man/systemd-cryptsetup-generator.xml | 14 ++++ + src/cryptsetup/cryptsetup-generator.c | 105 +++++++++++++++++++++++++- + 2 files changed, 115 insertions(+), 4 deletions(-) + +diff --git a/man/systemd-cryptsetup-generator.xml b/man/systemd-cryptsetup-generator.xml +index c37ee76b87..e30d69bfe7 100644 +--- a/man/systemd-cryptsetup-generator.xml ++++ b/man/systemd-cryptsetup-generator.xml +@@ -144,6 +144,20 @@ + to the one specified by rd.luks.key= or + luks.key= of the corresponding UUID, or the + password file that was specified without a UUID. ++ ++ It is also possible to specify an external device which ++ should be mounted before we attempt to unlock the LUKS device. ++ systemd-cryptsetup will use password file stored on that ++ device. Device containing password file is specified by ++ appending colon and a device identifier to the password file ++ path. For example, ++ rd.luks.uuid=b40f1abf-2a53-400a-889a-2eccc27eaa40 ++ rd.luks.key=b40f1abf-2a53-400a-889a-2eccc27eaa40=/keyfile:LABEL=keydev. ++ Hence, in this case, we will attempt to mount file system ++ residing on the block device with label keydev. ++ This syntax is for now only supported on a per-device basis, ++ i.e. you have to specify LUKS device UUID. ++ + rd.luks.key= + is honored only by initial RAM disk + (initrd) while +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index f5a81829b9..8c7a76e789 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -24,6 +24,7 @@ + typedef struct crypto_device { + char *uuid; + char *keyfile; ++ char *keydev; + char *name; + char *options; + bool create; +@@ -37,14 +38,71 @@ static Hashmap *arg_disks = NULL; + static char *arg_default_options = NULL; + static char *arg_default_keyfile = NULL; + ++static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) { ++ _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL; ++ _cleanup_fclose_ FILE *f = NULL; ++ int r; ++ ++ assert(name); ++ assert(keydev); ++ assert(unit); ++ assert(mount); ++ ++ r = mkdir_parents("/run/systemd/cryptsetup", 0755); ++ if (r < 0) ++ return r; ++ ++ r = mkdir("/run/systemd/cryptsetup", 0700); ++ if (r < 0) ++ return r; ++ ++ where = strjoin("/run/systemd/cryptsetup/keydev-", name); ++ if (!where) ++ return -ENOMEM; ++ ++ r = mkdir(where, 0700); ++ if (r < 0) ++ return r; ++ ++ r = unit_name_from_path(where, ".mount", &u); ++ if (r < 0) ++ return r; ++ ++ r = generator_open_unit_file(arg_dest, NULL, u, &f); ++ if (r < 0) ++ return r; ++ ++ what = fstab_node_to_udev_node(keydev); ++ if (!what) ++ return -ENOMEM; ++ ++ fprintf(f, ++ "[Unit]\n" ++ "DefaultDependencies=no\n\n" ++ "[Mount]\n" ++ "What=%s\n" ++ "Where=%s\n" ++ "Options=ro\n", what, where); ++ ++ r = fflush_and_check(f); ++ if (r < 0) ++ return r; ++ ++ *unit = TAKE_PTR(u); ++ *mount = TAKE_PTR(where); ++ ++ return 0; ++} ++ + static int create_disk( + const char *name, + const char *device, ++ const char *keydev, + const char *password, + const char *options) { + + _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL, +- *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL; ++ *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *dmname; + bool noauto, nofail, tmp, swap, netdev; +@@ -94,6 +152,9 @@ static int create_disk( + return log_oom(); + } + ++ if (keydev && !password) ++ return log_error_errno(-EINVAL, "Keydev is specified, but path to the password file is missing: %m"); ++ + r = generator_open_unit_file(arg_dest, NULL, n, &f); + if (r < 0) + return r; +@@ -109,6 +170,20 @@ static int create_disk( + "After=%s\n", + netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target"); + ++ if (keydev) { ++ _cleanup_free_ char *unit = NULL, *p = NULL; ++ ++ r = generate_keydev_mount(name, keydev, &unit, &keydev_mount); ++ if (r < 0) ++ return log_error_errno(r, "Failed to generate keydev mount unit: %m"); ++ ++ p = prefix_root(keydev_mount, password_escaped); ++ if (!p) ++ return log_oom(); ++ ++ free_and_replace(password_escaped, p); ++ } ++ + if (!nofail) + fprintf(f, + "Before=%s\n", +@@ -186,6 +261,11 @@ static int create_disk( + "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n", + name_escaped); + ++ if (keydev) ++ fprintf(f, ++ "ExecStartPost=" UMOUNT_PATH " %s\n\n", ++ keydev_mount); ++ + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", n); +@@ -221,6 +301,7 @@ static int create_disk( + static void crypt_device_free(crypto_device *d) { + free(d->uuid); + free(d->keyfile); ++ free(d->keydev); + free(d->name); + free(d->options); + free(d); +@@ -309,11 +390,27 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { ++ char *c; ++ _cleanup_free_ char *keyfile = NULL, *keydev = NULL; ++ + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + +- free_and_replace(d->keyfile, uuid_value); ++ c = strrchr(uuid_value, ':'); ++ if (!c) ++ /* No keydev specified */ ++ return free_and_replace(d->keyfile, uuid_value); ++ ++ *c = '\0'; ++ keyfile = strdup(uuid_value); ++ keydev = strdup(++c); ++ ++ if (!keyfile || !keydev) ++ return log_oom(); ++ ++ free_and_replace(d->keyfile, keyfile); ++ free_and_replace(d->keydev, keydev); + } else if (free_and_strdup(&arg_default_keyfile, value) < 0) + return log_oom(); + +@@ -394,7 +491,7 @@ static int add_crypttab_devices(void) { + continue; + } + +- r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options); ++ r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options); + if (r < 0) + return r; + +@@ -434,7 +531,7 @@ static int add_proc_cmdline_devices(void) { + else + options = "timeout=0"; + +- r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options); ++ r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options); + if (r < 0) + return r; + } diff --git a/SOURCES/0049-cryptsetup-don-t-use-m-if-there-s-no-error-to-show.patch b/SOURCES/0049-cryptsetup-don-t-use-m-if-there-s-no-error-to-show.patch new file mode 100644 index 0000000..28f8a27 --- /dev/null +++ b/SOURCES/0049-cryptsetup-don-t-use-m-if-there-s-no-error-to-show.patch @@ -0,0 +1,33 @@ +From 95bfd1d2f52698604e44c17dba2082f61b5f8eab Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Oct 2018 22:37:37 +0200 +Subject: [PATCH] cryptsetup: don't use %m if there's no error to show + +We are not the ones receiving an error here, but the ones generating it, +hence we shouldn't show it with %m, that's just confusing, as it +suggests we received an error from some other call. + +(cherry-picked from commit 2abe64666e544be6499f870618185f8819b4c152) + +Related: #1656869 +--- + src/cryptsetup/cryptsetup-generator.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index 8c7a76e789..52391bd185 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -152,8 +152,10 @@ static int create_disk( + return log_oom(); + } + +- if (keydev && !password) +- return log_error_errno(-EINVAL, "Keydev is specified, but path to the password file is missing: %m"); ++ if (keydev && !password) { ++ log_error("Key device is specified, but path to the password file is missing."); ++ return -EINVAL; ++ } + + r = generator_open_unit_file(arg_dest, NULL, n, &f); + if (r < 0) diff --git a/SOURCES/0050-cryptsetup-generator-don-t-return-error-if-target-di.patch b/SOURCES/0050-cryptsetup-generator-don-t-return-error-if-target-di.patch new file mode 100644 index 0000000..b9fa960 --- /dev/null +++ b/SOURCES/0050-cryptsetup-generator-don-t-return-error-if-target-di.patch @@ -0,0 +1,38 @@ +From 81df5f597257bd2579246de6182c4949b27396eb Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 4 Sep 2018 19:51:14 +0200 +Subject: [PATCH] cryptsetup-generator: don't return error if target directory + already exists + +(cherry-picked from commit 579875bc4a59b917fa32519e3d96d56dc591ad1e) + +Related: #1656869 +--- + src/cryptsetup/cryptsetup-generator.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index 52391bd185..03c513c26e 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -53,16 +53,16 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un + return r; + + r = mkdir("/run/systemd/cryptsetup", 0700); +- if (r < 0) +- return r; ++ if (r < 0 && errno != EEXIST) ++ return -errno; + + where = strjoin("/run/systemd/cryptsetup/keydev-", name); + if (!where) + return -ENOMEM; + + r = mkdir(where, 0700); +- if (r < 0) +- return r; ++ if (r < 0 && errno != EEXIST) ++ return -errno; + + r = unit_name_from_path(where, ".mount", &u); + if (r < 0) diff --git a/SOURCES/0051-cryptsetup-generator-allow-whitespace-characters-in-.patch b/SOURCES/0051-cryptsetup-generator-allow-whitespace-characters-in-.patch new file mode 100644 index 0000000..c67215a --- /dev/null +++ b/SOURCES/0051-cryptsetup-generator-allow-whitespace-characters-in-.patch @@ -0,0 +1,129 @@ +From 2a4d58bb2ab9ba5487785cc167932440a4f0c13d Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 4 Sep 2018 20:03:34 +0200 +Subject: [PATCH] cryptsetup-generator: allow whitespace characters in keydev + specification + +For example, =/keyfile:LABEL="KEYFILE FS" previously wouldn't +work, because we truncated label at the first whitespace character, +i.e. LABEL="KEYFILE". + +(cherry-picked from commit 7949dfa73a44ae6524779689483d12243dfbcfdf) + +Related: #1656869 +--- + src/cryptsetup/cryptsetup-generator.c | 64 ++++++++++++++++++--------- + 1 file changed, 43 insertions(+), 21 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index 03c513c26e..52c1262728 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -5,11 +5,13 @@ + + #include "alloc-util.h" + #include "dropin.h" ++#include "escape.h" + #include "fd-util.h" + #include "fileio.h" + #include "fstab-util.h" + #include "generator.h" + #include "hashmap.h" ++#include "id128-util.h" + #include "log.h" + #include "mkdir.h" + #include "parse-util.h" +@@ -39,7 +41,7 @@ static char *arg_default_options = NULL; + static char *arg_default_keyfile = NULL; + + static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) { +- _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL; ++ _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + +@@ -56,7 +58,11 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un + if (r < 0 && errno != EEXIST) + return -errno; + +- where = strjoin("/run/systemd/cryptsetup/keydev-", name); ++ name_escaped = cescape(name); ++ if (!name_escaped) ++ return -ENOMEM; ++ ++ where = strjoin("/run/systemd/cryptsetup/keydev-", name_escaped); + if (!where) + return -ENOMEM; + +@@ -386,36 +392,52 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + return log_oom(); + + } else if (streq(key, "luks.key")) { ++ size_t n; ++ _cleanup_free_ char *keyfile = NULL, *keydev = NULL; ++ char *c; ++ const char *keyspec; + + if (proc_cmdline_value_missing(key, value)) + return 0; + +- r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); +- if (r == 2) { +- char *c; +- _cleanup_free_ char *keyfile = NULL, *keydev = NULL; ++ n = strspn(value, LETTERS DIGITS "-"); ++ if (value[n] != '=') { ++ if (free_and_strdup(&arg_default_keyfile, value) < 0) ++ return log_oom(); ++ return 0; ++ } + +- d = get_crypto_device(uuid); +- if (!d) +- return log_oom(); ++ uuid = strndup(value, n); ++ if (!uuid) ++ return log_oom(); + +- c = strrchr(uuid_value, ':'); +- if (!c) +- /* No keydev specified */ +- return free_and_replace(d->keyfile, uuid_value); ++ if (!id128_is_valid(uuid)) { ++ log_warning("Failed to parse luks.key= kernel command line switch. UUID is invalid, ignoring."); ++ return 0; ++ } ++ ++ d = get_crypto_device(uuid); ++ if (!d) ++ return log_oom(); + +- *c = '\0'; +- keyfile = strdup(uuid_value); +- keydev = strdup(++c); ++ keyspec = value + n + 1; ++ c = strrchr(keyspec, ':'); ++ if (c) { ++ *c = '\0'; ++ keyfile = strdup(keyspec); ++ keydev = strdup(c + 1); + + if (!keyfile || !keydev) + return log_oom(); ++ } else { ++ /* No keydev specified */ ++ keyfile = strdup(keyspec); ++ if (!keyfile) ++ return log_oom(); ++ } + +- free_and_replace(d->keyfile, keyfile); +- free_and_replace(d->keydev, keydev); +- } else if (free_and_strdup(&arg_default_keyfile, value) < 0) +- return log_oom(); +- ++ free_and_replace(d->keyfile, keyfile); ++ free_and_replace(d->keydev, keydev); + } else if (streq(key, "luks.name")) { + + if (proc_cmdline_value_missing(key, value)) diff --git a/SOURCES/0052-rules-watch-metadata-changes-on-DASD-devices.patch b/SOURCES/0052-rules-watch-metadata-changes-on-DASD-devices.patch new file mode 100644 index 0000000..984e86c --- /dev/null +++ b/SOURCES/0052-rules-watch-metadata-changes-on-DASD-devices.patch @@ -0,0 +1,25 @@ +From c16785e970b83590fc9de4ea0f7e410470d88db5 Mon Sep 17 00:00:00 2001 +From: Vojtech Trefny +Date: Tue, 4 Dec 2018 16:47:36 +0100 +Subject: [PATCH] rules: watch metadata changes on DASD devices + +To make sure the change event is emitted and udev db is updated +after metadata changes. + +(cherry picked from commit 38397c8ce044fdc0138c9919168a856c0e16f720) + +Resolves: #1638676 +--- + rules/60-block.rules | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rules/60-block.rules b/rules/60-block.rules +index 343fc06f85..a1458e9188 100644 +--- a/rules/60-block.rules ++++ b/rules/60-block.rules +@@ -8,4 +8,4 @@ ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_ + ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" + + # watch metadata changes, caused by tools closing the device node which was opened for writing +-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*", OPTIONS+="watch" ++ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*|dasd*", OPTIONS+="watch" diff --git a/SOURCES/0053-sysctl.d-switch-net.ipv4.conf.all.rp_filter-from-1-t.patch b/SOURCES/0053-sysctl.d-switch-net.ipv4.conf.all.rp_filter-from-1-t.patch new file mode 100644 index 0000000..af96f5f --- /dev/null +++ b/SOURCES/0053-sysctl.d-switch-net.ipv4.conf.all.rp_filter-from-1-t.patch @@ -0,0 +1,41 @@ +From 75c9af80cf3529c76988451e63f98010c86f48f1 Mon Sep 17 00:00:00 2001 +From: Lubomir Rintel +Date: Wed, 28 Nov 2018 11:44:20 +0100 +Subject: [PATCH] sysctl.d: switch net.ipv4.conf.all.rp_filter from 1 to 2 + +This switches the RFC3704 Reverse Path filtering from Strict mode to Loose +mode. The Strict mode breaks some pretty common and reasonable use cases, +such as keeping connections via one default route alive after another one +appears (e.g. plugging an Ethernet cable when connected via Wi-Fi). + +The strict filter also makes it impossible for NetworkManager to do +connectivity check on a newly arriving default route (it starts with a +higher metric and is bumped lower if there's connectivity). + +Kernel's default is 0 (no filter), but a Loose filter is good enough. The +few use cases where a Strict mode could make sense can easily override +this. + +The distributions that don't care about the client use cases and prefer a +strict filter could just ship a custom configuration in +/usr/lib/sysctl.d/ to override this. + +Cherry-picked from: 230450d4e4f1f5fc9fa4295ed9185eea5b6ea16e +Resolves: #1653824 +--- + sysctl.d/50-default.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sysctl.d/50-default.conf b/sysctl.d/50-default.conf +index e263cf0628..b0645f33e7 100644 +--- a/sysctl.d/50-default.conf ++++ b/sysctl.d/50-default.conf +@@ -22,7 +22,7 @@ kernel.sysrq = 16 + kernel.core_uses_pid = 1 + + # Source route verification +-net.ipv4.conf.all.rp_filter = 1 ++net.ipv4.conf.all.rp_filter = 2 + + # Do not accept source routing + net.ipv4.conf.all.accept_source_route = 0 diff --git a/SOURCES/0054-tests-explicitly-enable-user-namespaces-for-TEST-13-.patch b/SOURCES/0054-tests-explicitly-enable-user-namespaces-for-TEST-13-.patch new file mode 100644 index 0000000..bc76352 --- /dev/null +++ b/SOURCES/0054-tests-explicitly-enable-user-namespaces-for-TEST-13-.patch @@ -0,0 +1,36 @@ +From e8ead61e1c0a919a97df64b14dbd572ef7c830d2 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Sat, 15 Dec 2018 20:22:31 +0100 +Subject: [PATCH] tests: explicitly enable user namespaces for + TEST-13-NSPAWN-SMOKE + +Cherry-picked from: 67f5c0c776ce9449ad21e9854665573a05141fd4 +--- + test/TEST-13-NSPAWN-SMOKE/test.sh | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh +index 6a0cb42eaf..c0789b5d20 100755 +--- a/test/TEST-13-NSPAWN-SMOKE/test.sh ++++ b/test/TEST-13-NSPAWN-SMOKE/test.sh +@@ -18,7 +18,7 @@ test_setup() { + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment +- dracut_install busybox chmod rmdir unshare ip ++ dracut_install busybox chmod rmdir unshare ip sysctl + + cp create-busybox-container $initdir/ + +@@ -63,6 +63,11 @@ if [[ -f /proc/1/ns/cgroup ]]; then + fi + + is_user_ns_supported=no ++# On some systems (e.g. CentOS 7) the default limit for user namespaces ++# is set to 0, which causes the following unshare syscall to fail, even ++# with enabled user namespaces support. By setting this value explicitly ++# we can ensure the user namespaces support to be detected correctly. ++sysctl -w user.max_user_namespaces=10000 + if unshare -U sh -c :; then + is_user_ns_supported=yes + fi diff --git a/SOURCES/0055-nspawn-beef-up-netns-checking-a-bit-for-compat-with-.patch b/SOURCES/0055-nspawn-beef-up-netns-checking-a-bit-for-compat-with-.patch new file mode 100644 index 0000000..2770487 --- /dev/null +++ b/SOURCES/0055-nspawn-beef-up-netns-checking-a-bit-for-compat-with-.patch @@ -0,0 +1,122 @@ +From 2115fcc1e673079fe76e949ac0904267075c25a4 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 31 Oct 2018 13:04:20 +0100 +Subject: [PATCH] nspawn: beef up netns checking a bit, for compat with old + kernels + +Fixes: #10544 + +Cherry-picked from: 6619ad889da260cf83079cc74a85d571acd1df5a +--- + src/basic/stat-util.c | 40 +++++++++++++++++++++++++++++++++++---- + src/nspawn/nspawn.c | 8 +++++--- + src/test/test-stat-util.c | 15 +++++++++++++++ + 3 files changed, 56 insertions(+), 7 deletions(-) + +diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c +index 07154e25bb..26aee9bad6 100644 +--- a/src/basic/stat-util.c ++++ b/src/basic/stat-util.c +@@ -204,15 +204,47 @@ int fd_is_network_fs(int fd) { + } + + int fd_is_network_ns(int fd) { ++ struct statfs s; + int r; + +- r = fd_is_fs_type(fd, NSFS_MAGIC); +- if (r <= 0) +- return r; ++ /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice ++ * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle ++ * this somewhat nicely. ++ * ++ * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not ++ * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */ ++ ++ if (fstatfs(fd, &s) < 0) ++ return -errno; ++ ++ if (!is_fs_type(&s, NSFS_MAGIC)) { ++ /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs ++ * instead. Handle that in a somewhat smart way. */ ++ ++ if (is_fs_type(&s, PROC_SUPER_MAGIC)) { ++ struct statfs t; ++ ++ /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the ++ * passed fd might refer to a network namespace, but we can't know for sure. In that case, ++ * return a recognizable error. */ ++ ++ if (statfs("/proc/self/ns/net", &t) < 0) ++ return -errno; ++ ++ if (s.f_type == t.f_type) ++ return -EUCLEAN; /* It's possible, we simply don't know */ ++ } ++ ++ return 0; /* No! */ ++ } + + r = ioctl(fd, NS_GET_NSTYPE); +- if (r < 0) ++ if (r < 0) { ++ if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */ ++ return -EUCLEAN; ++ + return -errno; ++ } + + return r == CLONE_NEWNET; + } +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index 56877bd932..8aec893a69 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -3701,10 +3701,12 @@ static int run(int master, + return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path); + + r = fd_is_network_ns(netns_fd); +- if (r < 0 && r != -ENOTTY) ++ if (r == -EUCLEAN) ++ log_debug_errno(r, "Cannot determine if passed network namespace path '%s' really refers to a network namespace, assuming it does.", arg_network_namespace_path); ++ else if (r < 0) + return log_error_errno(r, "Failed to check %s fs type: %m", arg_network_namespace_path); +- if (r == 0) { +- log_error("Path %s doesn't refer to a network namespace", arg_network_namespace_path); ++ else if (r == 0) { ++ log_error("Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path); + return -EINVAL; + } + } +diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c +index 43f56a6c20..2b0564d8a0 100644 +--- a/src/test/test-stat-util.c ++++ b/src/test/test-stat-util.c +@@ -67,11 +67,26 @@ static void test_path_is_temporary_fs(void) { + assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT); + } + ++static void test_fd_is_network_ns(void) { ++ _cleanup_close_ int fd = -1; ++ assert_se(fd_is_network_ns(STDIN_FILENO) == 0); ++ assert_se(fd_is_network_ns(STDERR_FILENO) == 0); ++ assert_se(fd_is_network_ns(STDOUT_FILENO) == 0); ++ ++ assert_se((fd = open("/proc/self/ns/mnt", O_CLOEXEC|O_RDONLY)) >= 0); ++ assert_se(IN_SET(fd_is_network_ns(fd), 0, -EUCLEAN)); ++ fd = safe_close(fd); ++ ++ assert_se((fd = open("/proc/self/ns/net", O_CLOEXEC|O_RDONLY)) >= 0); ++ assert_se(IN_SET(fd_is_network_ns(fd), 1, -EUCLEAN)); ++} ++ + int main(int argc, char *argv[]) { + test_files_same(); + test_is_symlink(); + test_path_is_fs_type(); + test_path_is_temporary_fs(); ++ test_fd_is_network_ns(); + + return 0; + } diff --git a/SOURCES/0056-test-Drop-SKIP_INITRD-for-QEMU-based-tests.patch b/SOURCES/0056-test-Drop-SKIP_INITRD-for-QEMU-based-tests.patch new file mode 100644 index 0000000..cea0065 --- /dev/null +++ b/SOURCES/0056-test-Drop-SKIP_INITRD-for-QEMU-based-tests.patch @@ -0,0 +1,93 @@ +From 13d819cc795d8c3695ce7288436ad569366073f6 Mon Sep 17 00:00:00 2001 +From: Michael Biebl +Date: Mon, 16 Jul 2018 11:27:44 +0200 +Subject: [PATCH] test: Drop SKIP_INITRD for QEMU-based tests + +Not all distros support booting without an initrd. E.g. the Debian +kernel builds ext4 as a module and so relies on an initrd to +successfully start the QEMU-based images. + +Cherry-picked from: c2d4da002095fe6f86f89a508a81e48fb6d3196f +--- + test/TEST-08-ISSUE-2730/test.sh | 1 - + test/TEST-09-ISSUE-2691/test.sh | 1 - + test/TEST-10-ISSUE-2467/test.sh | 1 - + test/TEST-11-ISSUE-3166/test.sh | 1 - + test/TEST-13-NSPAWN-SMOKE/test.sh | 2 +- + test/TEST-14-MACHINE-ID/test.sh | 2 +- + 6 files changed, 2 insertions(+), 6 deletions(-) + +diff --git a/test/TEST-08-ISSUE-2730/test.sh b/test/TEST-08-ISSUE-2730/test.sh +index 68159c331f..90bf133c6a 100755 +--- a/test/TEST-08-ISSUE-2730/test.sh ++++ b/test/TEST-08-ISSUE-2730/test.sh +@@ -6,7 +6,6 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730" + TEST_NO_NSPAWN=1 + + . $TEST_BASE_DIR/test-functions +-SKIP_INITRD=yes + QEMU_TIMEOUT=180 + FSTYPE=ext4 + +diff --git a/test/TEST-09-ISSUE-2691/test.sh b/test/TEST-09-ISSUE-2691/test.sh +index 4c3e9496b4..9b5990bc60 100755 +--- a/test/TEST-09-ISSUE-2691/test.sh ++++ b/test/TEST-09-ISSUE-2691/test.sh +@@ -6,7 +6,6 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2691" + TEST_NO_NSPAWN=1 + + . $TEST_BASE_DIR/test-functions +-SKIP_INITRD=yes + QEMU_TIMEOUT=90 + + test_setup() { +diff --git a/test/TEST-10-ISSUE-2467/test.sh b/test/TEST-10-ISSUE-2467/test.sh +index 2f95e9062d..e61f5acd3c 100755 +--- a/test/TEST-10-ISSUE-2467/test.sh ++++ b/test/TEST-10-ISSUE-2467/test.sh +@@ -5,7 +5,6 @@ set -e + TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467" + + . $TEST_BASE_DIR/test-functions +-SKIP_INITRD=yes + + test_setup() { + create_empty_image +diff --git a/test/TEST-11-ISSUE-3166/test.sh b/test/TEST-11-ISSUE-3166/test.sh +index 4602bdfc98..8aae4d5ed9 100755 +--- a/test/TEST-11-ISSUE-3166/test.sh ++++ b/test/TEST-11-ISSUE-3166/test.sh +@@ -6,7 +6,6 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/3166" + TEST_NO_NSPAWN=1 + + . $TEST_BASE_DIR/test-functions +-SKIP_INITRD=yes + + test_setup() { + create_empty_image +diff --git a/test/TEST-13-NSPAWN-SMOKE/test.sh b/test/TEST-13-NSPAWN-SMOKE/test.sh +index c0789b5d20..a676384bfc 100755 +--- a/test/TEST-13-NSPAWN-SMOKE/test.sh ++++ b/test/TEST-13-NSPAWN-SMOKE/test.sh +@@ -4,7 +4,7 @@ + set -e + TEST_DESCRIPTION="systemd-nspawn smoke test" + TEST_NO_NSPAWN=1 +-SKIP_INITRD=yes ++ + . $TEST_BASE_DIR/test-functions + + test_setup() { +diff --git a/test/TEST-14-MACHINE-ID/test.sh b/test/TEST-14-MACHINE-ID/test.sh +index 7342645bc5..62003b91b6 100755 +--- a/test/TEST-14-MACHINE-ID/test.sh ++++ b/test/TEST-14-MACHINE-ID/test.sh +@@ -4,7 +4,7 @@ + set -e + TEST_DESCRIPTION="/etc/machine-id testing" + TEST_NO_NSPAWN=1 +-SKIP_INITRD=yes ++ + . $TEST_BASE_DIR/test-functions + + test_setup() { diff --git a/SOURCES/0057-meson-rename-Ddebug-to-Ddebug-extra.patch b/SOURCES/0057-meson-rename-Ddebug-to-Ddebug-extra.patch new file mode 100644 index 0000000..19ae06f --- /dev/null +++ b/SOURCES/0057-meson-rename-Ddebug-to-Ddebug-extra.patch @@ -0,0 +1,42 @@ +From 9c1b72de44e68ad80be7c0b98df110e7b127072d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 19 Aug 2018 19:11:30 +0200 +Subject: [PATCH] meson: rename -Ddebug to -Ddebug-extra + +Meson added -Doptimization and -Ddebug options, which obviously causes +a conflict with our -Ddebug options. Let's rename it. + +Fixes #9883. + +Cherry-picked from: 8f6b442a78d0b485f044742ad90b2e8271b4e68e +--- + meson.build | 2 +- + meson_options.txt | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/meson.build b/meson.build +index f308db2631..ebc55872c9 100644 +--- a/meson.build ++++ b/meson.build +@@ -769,7 +769,7 @@ substs.set('DEBUGTTY', get_option('debug-tty')) + + enable_debug_hashmap = false + enable_debug_mmap_cache = false +-foreach name : get_option('debug') ++foreach name : get_option('debug-extra') + if name == 'hashmap' + enable_debug_hashmap = true + elif name == 'mmap-cache' +diff --git a/meson_options.txt b/meson_options.txt +index ab2a658713..5716f45ccf 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -46,7 +46,7 @@ option('debug-shell', type : 'string', value : '/bin/sh', + description : 'path to debug shell binary') + option('debug-tty', type : 'string', value : '/dev/tty9', + description : 'specify the tty device for debug shell') +-option('debug', type : 'array', choices : ['hashmap', 'mmap-cache'], value : [], ++option('debug-extra', type : 'array', choices : ['hashmap', 'mmap-cache'], value : [], + description : 'enable extra debugging') + option('memory-accounting-default', type : 'boolean', + description : 'enable MemoryAccounting= by default') diff --git a/SOURCES/0058-meson-check-whether-gnutls-supports-TCP-fast-open.patch b/SOURCES/0058-meson-check-whether-gnutls-supports-TCP-fast-open.patch new file mode 100644 index 0000000..e0c88dd --- /dev/null +++ b/SOURCES/0058-meson-check-whether-gnutls-supports-TCP-fast-open.patch @@ -0,0 +1,38 @@ +From b6943446f8ffde53ce059b5e869c22bed8926827 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 25 Jun 2018 22:40:40 +0900 +Subject: [PATCH] meson: check whether gnutls supports TCP fast open + +Fixes #9403 + +Cherry-picked from: f02582f69fe1e7663a87ba80bd4f90d5d23ee75f +--- + README | 1 + + meson.build | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/README b/README +index 2cde08c37e..7d06e04800 100644 +--- a/README ++++ b/README +@@ -154,6 +154,7 @@ REQUIREMENTS: + libmicrohttpd (optional) + libpython (optional) + libidn2 or libidn (optional) ++ gnutls >= 3.1.4 (optional, >= 3.5.3 is necessary to support DNS-over-TLS) + elfutils >= 158 (optional) + polkit (optional) + pkg-config +diff --git a/meson.build b/meson.build +index ebc55872c9..d58926c981 100644 +--- a/meson.build ++++ b/meson.build +@@ -1148,7 +1148,7 @@ substs.set('DEFAULT_DNSSEC_MODE', default_dnssec) + + dns_over_tls = get_option('dns-over-tls') + if dns_over_tls != 'false' +- have = conf.get('HAVE_GNUTLS') == 1 ++ have = libgnutls != [] and libgnutls.version().version_compare('>=3.5.3') + if dns_over_tls == 'true' and not have + error('DNS-over-TLS support was requested, but dependencies are not available') + endif diff --git a/SOURCES/0059-unit-don-t-add-Requires-for-tmp.mount.patch b/SOURCES/0059-unit-don-t-add-Requires-for-tmp.mount.patch new file mode 100644 index 0000000..6712934 --- /dev/null +++ b/SOURCES/0059-unit-don-t-add-Requires-for-tmp.mount.patch @@ -0,0 +1,24 @@ +From 03e52d33bbdea731eaa79545bb1d30c5b21abe3d Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Mon, 5 Sep 2016 12:47:09 +0200 +Subject: [PATCH] unit: don't add Requires for tmp.mount + +rhel-only +Resolves: #1619292 +--- + src/core/unit.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index c9f756c9c7..721d8d60a3 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1421,7 +1421,7 @@ static int unit_add_mount_dependencies(Unit *u) { + if (r < 0) + return r; + +- if (m->fragment_path) { ++ if (m->fragment_path && !streq(m->id, "tmp.mount")) { + r = unit_add_dependency(u, UNIT_REQUIRES, m, true, di.origin_mask); + if (r < 0) + return r; diff --git a/SOURCES/0060-tests-drop-the-precondition-check-for-inherited-flag.patch b/SOURCES/0060-tests-drop-the-precondition-check-for-inherited-flag.patch new file mode 100644 index 0000000..b921532 --- /dev/null +++ b/SOURCES/0060-tests-drop-the-precondition-check-for-inherited-flag.patch @@ -0,0 +1,42 @@ +From 1d43806017a0df257fef8ed6f79e12ee69c5bc20 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Thu, 8 Nov 2018 09:40:13 +0100 +Subject: [PATCH] tests: drop the precondition check for inherited flag + +Docker's default capability set has the inherited flag already +set - that breaks tests which expect otherwise. Let's just +drop the check and run the test anyway. + +Fixes #10663 + +Cherry-picked from: c446b8486d9ed18d1bc780948ae9ee8a53fa4c3f +--- + src/test/test-capability.c | 8 -------- + 1 file changed, 8 deletions(-) + +diff --git a/src/test/test-capability.c b/src/test/test-capability.c +index af6d808b6d..72975cef94 100644 +--- a/src/test/test-capability.c ++++ b/src/test/test-capability.c +@@ -180,8 +180,6 @@ static void test_update_inherited_set(void) { + + caps = cap_get_proc(); + assert_se(caps); +- assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv)); +- assert(fv == CAP_CLEAR); + + set = (UINT64_C(1) << CAP_CHOWN); + +@@ -197,12 +195,6 @@ static void test_set_ambient_caps(void) { + uint64_t set = 0; + cap_flag_value_t fv; + +- caps = cap_get_proc(); +- assert_se(caps); +- assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv)); +- assert(fv == CAP_CLEAR); +- cap_free(caps); +- + assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0); + + set = (UINT64_C(1) << CAP_CHOWN); diff --git a/SOURCES/0061-core-when-deserializing-state-always-use-read_line-L.patch b/SOURCES/0061-core-when-deserializing-state-always-use-read_line-L.patch new file mode 100644 index 0000000..9a53203 --- /dev/null +++ b/SOURCES/0061-core-when-deserializing-state-always-use-read_line-L.patch @@ -0,0 +1,234 @@ +From 55a1c766445750aaefe28bd7bea454f5f1cff9bb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 17 Oct 2018 18:36:24 +0200 +Subject: [PATCH] =?UTF-8?q?core:=20when=20deserializing=20state=20always?= + =?UTF-8?q?=20use=20read=5Fline(=E2=80=A6,=20LONG=5FLINE=5FMAX,=20?= + =?UTF-8?q?=E2=80=A6)?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This should be much better than fgets(), as we can read substantially +longer lines and overly long lines result in proper errors. + +Fixes a vulnerability discovered by Jann Horn at Google. + +CVE-2018-15686 +LP: #1796402 +https://bugzilla.redhat.com/show_bug.cgi?id=1639071 + +(cherry picked from commit 8948b3415d762245ebf5e19d80b97d4d8cc208c1) + +Resolves: CVE-2018-15686 +--- + src/core/job.c | 19 +++++++++++-------- + src/core/manager.c | 47 ++++++++++++++++++++-------------------------- + src/core/unit.c | 34 +++++++++++++++++---------------- + src/core/unit.h | 2 +- + 4 files changed, 50 insertions(+), 52 deletions(-) + +diff --git a/src/core/job.c b/src/core/job.c +index 734756b666..8552ffb704 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -10,6 +10,7 @@ + #include "dbus-job.h" + #include "dbus.h" + #include "escape.h" ++#include "fileio.h" + #include "job.h" + #include "log.h" + #include "macro.h" +@@ -1091,24 +1092,26 @@ int job_serialize(Job *j, FILE *f) { + } + + int job_deserialize(Job *j, FILE *f) { ++ int r; ++ + assert(j); + assert(f); + + for (;;) { +- char line[LINE_MAX], *l, *v; ++ _cleanup_free_ char *line = NULL; ++ char *l, *v; + size_t k; + +- if (!fgets(line, sizeof(line), f)) { +- if (feof(f)) +- return 0; +- return -errno; +- } ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read serialization line: %m"); ++ if (r == 0) ++ return 0; + +- char_array_0(line); + l = strstrip(line); + + /* End marker */ +- if (l[0] == 0) ++ if (isempty(l)) + return 0; + + k = strcspn(l, "="); +diff --git a/src/core/manager.c b/src/core/manager.c +index 3b2fe11e87..c83e296cf3 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -3144,22 +3144,17 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + m->n_reloading++; + + for (;;) { +- char line[LINE_MAX]; ++ _cleanup_free_ char *line = NULL; + const char *val, *l; + +- if (!fgets(line, sizeof(line), f)) { +- if (feof(f)) +- r = 0; +- else +- r = -errno; +- +- goto finish; +- } ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read serialization line: %m"); ++ if (r == 0) ++ break; + +- char_array_0(line); + l = strstrip(line); +- +- if (l[0] == 0) ++ if (isempty(l)) /* end marker */ + break; + + if ((val = startswith(l, "current-job-id="))) { +@@ -3326,29 +3321,27 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + } + + for (;;) { +- Unit *u; +- char name[UNIT_NAME_MAX+2]; ++ _cleanup_free_ char *line = NULL; + const char* unit_name; ++ Unit *u; + + /* Start marker */ +- if (!fgets(name, sizeof(name), f)) { +- if (feof(f)) +- r = 0; +- else +- r = -errno; +- +- goto finish; +- } ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read serialization line: %m"); ++ if (r == 0) ++ break; + +- char_array_0(name); +- unit_name = strstrip(name); ++ unit_name = strstrip(line); + + r = manager_load_unit(m, unit_name, NULL, NULL, &u); + if (r < 0) { + log_notice_errno(r, "Failed to load unit \"%s\", skipping deserialization: %m", unit_name); +- if (r == -ENOMEM) +- goto finish; +- unit_deserialize_skip(f); ++ ++ r = unit_deserialize_skip(f); ++ if (r < 0) ++ return r; ++ + continue; + } + +diff --git a/src/core/unit.c b/src/core/unit.c +index 721d8d60a3..cc43ddc4f1 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -3368,21 +3368,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + assert(fds); + + for (;;) { +- char line[LINE_MAX], *l, *v; ++ _cleanup_free_ char *line = NULL; + CGroupIPAccountingMetric m; ++ char *l, *v; + size_t k; + +- if (!fgets(line, sizeof(line), f)) { +- if (feof(f)) +- return 0; +- return -errno; +- } ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read serialization line: %m"); ++ if (r == 0) /* eof */ ++ break; + +- char_array_0(line); + l = strstrip(line); +- +- /* End marker */ +- if (isempty(l)) ++ if (isempty(l)) /* End marker */ + break; + + k = strcspn(l, "="); +@@ -3657,23 +3655,27 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + return 0; + } + +-void unit_deserialize_skip(FILE *f) { ++int unit_deserialize_skip(FILE *f) { ++ int r; + assert(f); + + /* Skip serialized data for this unit. We don't know what it is. */ + + for (;;) { +- char line[LINE_MAX], *l; ++ _cleanup_free_ char *line = NULL; ++ char *l; + +- if (!fgets(line, sizeof line, f)) +- return; ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read serialization line: %m"); ++ if (r == 0) ++ return 0; + +- char_array_0(line); + l = strstrip(line); + + /* End marker */ + if (isempty(l)) +- return; ++ return 1; + } + } + +diff --git a/src/core/unit.h b/src/core/unit.h +index b3131eba1b..e1a60da244 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -679,7 +679,7 @@ bool unit_can_serialize(Unit *u) _pure_; + + int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs); + int unit_deserialize(Unit *u, FILE *f, FDSet *fds); +-void unit_deserialize_skip(FILE *f); ++int unit_deserialize_skip(FILE *f); + + int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value); + int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value); diff --git a/SOURCES/0062-core-enforce-a-limit-on-STATUS-texts-recvd-from-serv.patch b/SOURCES/0062-core-enforce-a-limit-on-STATUS-texts-recvd-from-serv.patch new file mode 100644 index 0000000..6d009c4 --- /dev/null +++ b/SOURCES/0062-core-enforce-a-limit-on-STATUS-texts-recvd-from-serv.patch @@ -0,0 +1,44 @@ +From 6abfec31acae53943896b309db4a09a1cecac9a3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 17 Oct 2018 18:37:48 +0200 +Subject: [PATCH] core: enforce a limit on STATUS= texts recvd from services + +Let's better be safe than sorry, and put a limit on what we receive. + +(cherry picked from commit 3eac1bcae9284fb8b18f4b82156c0e85ddb004e5) + +Related: CVE-2018-15686 +--- + src/core/service.c | 8 ++++++-- + src/core/service.h | 2 ++ + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index db1356c417..db17221888 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -3549,8 +3549,12 @@ static void service_notify_message( + _cleanup_free_ char *t = NULL; + + if (!isempty(e)) { +- if (!utf8_is_valid(e)) +- log_unit_warning(u, "Status message in notification message is not UTF-8 clean."); ++ /* Note that this size limit check is mostly paranoia: since the datagram size we are willing ++ * to process is already limited to NOTIFY_BUFFER_MAX, this limit here should never be hit. */ ++ if (strlen(e) > STATUS_TEXT_MAX) ++ log_unit_warning(u, "Status message overly long (%zu > %u), ignoring.", strlen(e), STATUS_TEXT_MAX); ++ else if (!utf8_is_valid(e)) ++ log_unit_warning(u, "Status message in notification message is not UTF-8 clean, ignoring."); + else { + t = strdup(e); + if (!t) +diff --git a/src/core/service.h b/src/core/service.h +index 9c06e91883..a142b09f0d 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -202,3 +202,5 @@ const char* service_result_to_string(ServiceResult i) _const_; + ServiceResult service_result_from_string(const char *s) _pure_; + + DEFINE_CAST(SERVICE, Service); ++ ++#define STATUS_TEXT_MAX (16U*1024U) diff --git a/SOURCES/0063-travis-enable-Travis-CI-on-CentOS-7.patch b/SOURCES/0063-travis-enable-Travis-CI-on-CentOS-7.patch new file mode 100644 index 0000000..4ba9fb3 --- /dev/null +++ b/SOURCES/0063-travis-enable-Travis-CI-on-CentOS-7.patch @@ -0,0 +1,257 @@ +From 5638e18196be1fabd9e78d4c506402bf700fe569 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 7 Jan 2019 15:49:45 +0100 +Subject: [PATCH] travis: enable Travis CI on CentOS 7 + +(cherry picked from commit 2014cb51b6dfe1f7f0b98e62311398c2bf801c2b) +--- + .travis.yml | 86 ++++++++++----------------------------------- + ci/travis-centos.sh | 69 ++++++++++++++++++++++++++++++++++++ + ci/travis_wait.bash | 61 ++++++++++++++++++++++++++++++++ + 3 files changed, 149 insertions(+), 67 deletions(-) + create mode 100755 ci/travis-centos.sh + create mode 100644 ci/travis_wait.bash + +diff --git a/.travis.yml b/.travis.yml +index d980038181..fc63887324 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -1,77 +1,29 @@ + sudo: required +- + services: + - docker + +-language: c ++env: ++ global: ++ - CI_ROOT="$TRAVIS_BUILD_DIR/ci/" + + jobs: + include: +- - stage: coverity scan +- before_script: +- - sudo apt-get update ++ - stage: Build & test ++ name: CentOS 7 ++ language: bash ++ env: ++ - CENTOS_RELEASE="centos7" ++ - CONT_NAME="systemd-centos-$CENTOS_RELEASE" ++ - DOCKER_EXEC="docker exec -ti $CONT_NAME" ++ before_install: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version +- - env > .env +- env: +- - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG" +- - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}" +- - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH" +- # Encrypted token for systemd/systemd Coverity Scan Analysis, +- # generated by "travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=" +- - secure: "lM0IVP2zOG5Ywk3YCbDCQL4WioyzzwtdtpZ+hKDy4BWCZDBJ/FVwIeBsXdMDvlTa3xi+GQ1b7kS2OmTfmG4aSlhU7isuH8SMq1Y4GR5AxfhkR+irUA1A1fntlvhbjIumDGW5wjs0Dt8KogMWS+ZD4eGE59lrVO/TrhMzIe1eHENVLFQJdNq+ZJXU8wxMfHf8lXk0xA8SJTid0XvZBNc4JN6pjJRA8LaOrMNhQYfygFmVQ598kwlu7gf5vbCKFPnIgJAxdIhz12XS9utGohV28IYj9d1DdUGUT+ar3OfADj3X8KFBP4Ymc02pcln3wVgdPtrDbFZh1R9jbmfdXGAH/6tTOJVn8aFySS2Vq9QiBiprWdPsAOLcWMNhnp0lMkASxs9/W26nU7Czo8VbAVWXM1w35plDpnDGR6lk/06dmOZpqu5p3AYr5xIKACIAdPDn0rNpnSWqC750WZ8ZWbHnKuZC5TWML7scVaPiEi7D7rbwqML2rdwx4ZoTZmCHiGByXCIWTfhf0JNQAix5WW3znl+BmDesumPgPj2mX+y6J1WYJrIz12m7qh7KhV/a1ODKM+I91A9rkOA/bPnmhmSSUR7CwgvZt1fC/VwBnaFFtAz9/70kN9Q8tDBXtXidExZwh1e3t5vDG72k3lXwNqpKRvdW3LOxK6lFvqEdMWVUJls=" ++ install: ++ - $CI_ROOT/travis-centos.sh SETUP + script: +- # Copy content of CI_DIR into WORKDIR +- - find $CI_DIR -maxdepth 1 -type f -exec cp -t . {} + +- # Build container for current user +- - $CI_SCRIPT_DIR/build-docker-image.sh +- +- # For kernel version 4.8+ +- - sudo sysctl vsyscall=emulate || true +- # Prepare environment for Coverity tool +- - | +- PLATFORM=`uname` +- export TOOL_BASE="/tmp/coverity-scan-analysis" +- export SCAN_URL="https://scan.coverity.com" +- export UPLOAD_URL="https://scan.coverity.com/builds" +- export TOOL_ARCHIVE="/tmp/cov-analysis-${PLATFORM}.tgz" +- +- # Get Coverity tool +- - $CI_TOOL_DIR/get-coverity.sh +- - TOOL_DIR="$(find $TOOL_BASE -type d -name 'cov-analysis*')" +- +- # Export env variables for Coverity scan +- - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env +- - | +- docker run -dit --env-file .cov-env \ +- -v ${TOOL_BASE}:${TOOL_BASE}:ro \ +- --name travis_coverity_scan coverity-${TRAVIS_COMMIT}:latest bash +- # Make sure Coverity script is executable +- - docker cp tools/coverity.sh travis_coverity_scan:/usr/local/bin +- # Preconfigure with meson to prevent Coverity from capturing meson metadata +- # Set compiler flag to prevent emit failure +- - docker exec -it travis_coverity_scan sh -c "CFLAGS='-D_Float128=long\ double -D_Float64=double -D_Float64x=long\ double -D_Float32=float -D_Float32x=double' meson cov-build -Dman=false" +- # Run Coverity Analysis +- - docker exec -it travis_coverity_scan coverity.sh build +- - docker exec -it travis_coverity_scan coverity.sh upload +- +-# Specify the order of stages and conditions +-stages: +- - name: coverity scan +- if: type = cron +- +-env: +- global: +- - ADMIN_EMAIL=macermak@redhat.com +- +- - AUTHOR_NAME="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aN\")" +- - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")" +- +- - CI_DIR="$TRAVIS_BUILD_DIR/travis-ci" +- - CI_TOOL_DIR="$CI_DIR/tools" +- - CI_SCRIPT_DIR="$CI_DIR/scripts" +- +-notifications: +- email: +- recipients: +- - ${ADMIN_EMAIL} ++ - set -e ++ # Build systemd ++ - $CI_ROOT/travis-centos.sh RUN ++ - set +e ++ after_script: ++ - $CI_ROOT/travis-centos.sh CLEANUP +diff --git a/ci/travis-centos.sh b/ci/travis-centos.sh +new file mode 100755 +index 0000000000..60bbdf14c2 +--- /dev/null ++++ b/ci/travis-centos.sh +@@ -0,0 +1,69 @@ ++#!/bin/bash ++ ++# Run this script from the root of the systemd's git repository ++# or set REPO_ROOT to a correct path. ++# ++# Example execution on Fedora: ++# dnf install docker ++# systemctl start docker ++# export CONT_NAME="my-fancy-container" ++# ci/travis-centos.sh SETUP RUN CLEANUP ++ ++PHASES=(${@:-SETUP RUN CLEANUP}) ++CENTOS_RELEASE="${CENTOS_RELEASE:-latest}" ++CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" ++DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" ++DOCKER_RUN="${DOCKER_RUN:-docker run}" ++REPO_ROOT="${REPO_ROOT:-$PWD}" ++ADDITIONAL_DEPS=(yum-utils iputils hostname libasan libubsan clang llvm) ++ ++function info() { ++ echo -e "\033[33;1m$1\033[0m" ++} ++ ++set -e ++ ++source "$(dirname $0)/travis_wait.bash" ++ ++for phase in "${PHASES[@]}"; do ++ case $phase in ++ SETUP) ++ info "Setup phase" ++ info "Using Travis $CENTOS_RELEASE" ++ # Pull a Docker image and start a new container ++ docker pull centos:$CENTOS_RELEASE ++ info "Starting container $CONT_NAME" ++ $DOCKER_RUN -v $REPO_ROOT:/build:rw \ ++ -w /build --privileged=true --name $CONT_NAME \ ++ -dit --net=host centos:$CENTOS_RELEASE /sbin/init ++ # Beautiful workaround for Fedora's version of Docker ++ sleep 1 ++ $DOCKER_EXEC yum makecache ++ # Install necessary build/test requirements ++ $DOCKER_EXEC yum -y --exclude selinux-policy\* upgrade ++ $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" ++ $DOCKER_EXEC yum-builddep -y systemd ++ ;; ++ RUN) ++ info "Run phase" ++ # Build systemd ++ $DOCKER_EXEC ./autogen.sh ++ $DOCKER_EXEC ./configure --disable-timesyncd --disable-kdbus --disable-terminal \ ++ --enable-gtk-doc --enable-compat-libs --disable-sysusers \ ++ --disable-ldconfig --enable-lz4 --with-sysvinit-path=/etc/rc.d/init.d ++ $DOCKER_EXEC make ++ if ! $DOCKER_EXEC make check; then ++ $DOCKER_EXEC cat test-suite.log ++ exit 1 ++ fi ++ ;; ++ CLEANUP) ++ info "Cleanup phase" ++ docker stop $CONT_NAME ++ docker rm -f $CONT_NAME ++ ;; ++ *) ++ echo >&2 "Unknown phase '$phase'" ++ exit 1 ++ esac ++done +diff --git a/ci/travis_wait.bash b/ci/travis_wait.bash +new file mode 100644 +index 0000000000..acf6ad15e4 +--- /dev/null ++++ b/ci/travis_wait.bash +@@ -0,0 +1,61 @@ ++# This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash ++# to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed ++# as soon as Travis CI has started to provide an easy way to export the functions to bash scripts. ++ ++travis_jigger() { ++ local cmd_pid="${1}" ++ shift ++ local timeout="${1}" ++ shift ++ local count=0 ++ ++ echo -e "\\n" ++ ++ while [[ "${count}" -lt "${timeout}" ]]; do ++ count="$((count + 1))" ++ echo -ne "Still running (${count} of ${timeout}): ${*}\\r" ++ sleep 60 ++ done ++ ++ echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n" ++ kill -9 "${cmd_pid}" ++} ++ ++travis_wait() { ++ local timeout="${1}" ++ ++ if [[ "${timeout}" =~ ^[0-9]+$ ]]; then ++ shift ++ else ++ timeout=20 ++ fi ++ ++ local cmd=("${@}") ++ local log_file="travis_wait_${$}.log" ++ ++ "${cmd[@]}" &>"${log_file}" & ++ local cmd_pid="${!}" ++ ++ travis_jigger "${!}" "${timeout}" "${cmd[@]}" & ++ local jigger_pid="${!}" ++ local result ++ ++ { ++ set +e ++ wait "${cmd_pid}" 2>/dev/null ++ result="${?}" ++ ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}" ++ set -e ++ } ++ ++ if [[ "${result}" -eq 0 ]]; then ++ echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" ++ else ++ echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" ++ fi ++ ++ echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n" ++ cat "${log_file}" ++ ++ return "${result}" ++} diff --git a/SOURCES/0064-travis-RHEL8-support.patch b/SOURCES/0064-travis-RHEL8-support.patch new file mode 100644 index 0000000..0ce198f --- /dev/null +++ b/SOURCES/0064-travis-RHEL8-support.patch @@ -0,0 +1,175 @@ +From 45b0a38b47e07186dfe35095c7d8b1e4c2524d80 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 14 Jan 2019 14:49:32 +0100 +Subject: [PATCH] travis: RHEL8 support + +(cherry picked from commit e5c78840b2b124400f56cb5fbaf2357cd8901218) +--- + .travis.yml | 8 +- + ...ravis-centos.sh => travis-centos-rhel7.sh} | 0 + ci/travis-centos-rhel8.sh | 130 ++++++++++++++++++ + 3 files changed, 135 insertions(+), 3 deletions(-) + rename ci/{travis-centos.sh => travis-centos-rhel7.sh} (100%) + create mode 100755 ci/travis-centos-rhel8.sh + +diff --git a/.travis.yml b/.travis.yml +index fc63887324..1c4e6f9728 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -19,11 +19,13 @@ jobs: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version + install: +- - $CI_ROOT/travis-centos.sh SETUP ++ - RHEL_VERSION="rhel7" ++ - [ -f meson.build ] && RHEL_VERSION="rhel8" ++ - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh SETUP + script: + - set -e + # Build systemd +- - $CI_ROOT/travis-centos.sh RUN ++ - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh RUN + - set +e + after_script: +- - $CI_ROOT/travis-centos.sh CLEANUP ++ - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh CLEANUP +diff --git a/ci/travis-centos.sh b/ci/travis-centos-rhel7.sh +similarity index 100% +rename from ci/travis-centos.sh +rename to ci/travis-centos-rhel7.sh +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +new file mode 100755 +index 0000000000..968603f949 +--- /dev/null ++++ b/ci/travis-centos-rhel8.sh +@@ -0,0 +1,130 @@ ++#!/bin/bash ++ ++# Run this script from the root of the systemd's git repository ++# or set REPO_ROOT to a correct path. ++# ++# Example execution on Fedora: ++# dnf install docker ++# systemctl start docker ++# export CONT_NAME="my-fancy-container" ++# ci/travis-centos.sh SETUP RUN CLEANUP ++ ++PHASES=(${@:-SETUP RUN CLEANUP}) ++CENTOS_RELEASE="${CENTOS_RELEASE:-latest}" ++CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" ++DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" ++DOCKER_RUN="${DOCKER_RUN:-docker run}" ++REPO_ROOT="${REPO_ROOT:-$PWD}" ++ADDITIONAL_DEPS=(systemd-ci-environment libidn2-devel python-lxml python36 ninja-build libasan net-tools strace nc busybox e2fsprogs quota dnsmasq) ++# Repo with additional depencencies to compile newer systemd on CentOS 7 ++COPR_REPO="https://copr.fedorainfracloud.org/coprs/mrc0mmand/systemd-centos-ci/repo/epel-7/mrc0mmand-systemd-centos-ci-epel-7.repo" ++COPR_REPO_PATH="/etc/yum.repos.d/${COPR_REPO##*/}" ++ ++function info() { ++ echo -e "\033[33;1m$1\033[0m" ++} ++ ++set -e ++ ++source "$(dirname $0)/travis_wait.bash" ++ ++for phase in "${PHASES[@]}"; do ++ case $phase in ++ SETUP) ++ info "Setup phase" ++ info "Using Travis $CENTOS_RELEASE" ++ # Pull a Docker image and start a new container ++ docker pull centos:$CENTOS_RELEASE ++ info "Starting container $CONT_NAME" ++ $DOCKER_RUN -v $REPO_ROOT:/build:rw \ ++ -w /build --privileged=true --name $CONT_NAME \ ++ -dit --net=host centos:$CENTOS_RELEASE /sbin/init ++ # Beautiful workaround for Fedora's version of Docker ++ sleep 1 ++ $DOCKER_EXEC yum makecache ++ $DOCKER_EXEC curl "$COPR_REPO" -o "$COPR_REPO_PATH" ++ $DOCKER_EXEC yum -q -y install epel-release yum-utils ++ $DOCKER_EXEC yum-config-manager -q --enable epel ++ $DOCKER_EXEC yum -y --exclude selinux-policy\* upgrade ++ # Install necessary build/test requirements ++ $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" ++ $DOCKER_EXEC python3.6 -m ensurepip ++ $DOCKER_EXEC python3.6 -m pip install meson ++ # Create necessary symlinks ++ $DOCKER_EXEC ln --force -s /usr/bin/python3.6 /usr/bin/python3 ++ $DOCKER_EXEC ln --force -s /usr/bin/ninja-build /usr/bin/ninja ++ ;; ++ RUN) ++ info "Run phase" ++ # Build systemd ++ CONFIGURE_OPTS=( ++ # RHEL8 options ++ -Dsysvinit-path=/etc/rc.d/init.d ++ -Drc-local=/etc/rc.d/rc.local ++ -Ddns-servers='' ++ -Ddev-kvm-mode=0666 ++ -Dkmod=true ++ -Dxkbcommon=true ++ -Dblkid=true ++ -Dseccomp=true ++ -Dima=true ++ -Dselinux=true ++ -Dapparmor=false ++ -Dpolkit=true ++ -Dxz=true ++ -Dzlib=true ++ -Dbzip2=true ++ -Dlz4=true ++ -Dpam=true ++ -Dacl=true ++ -Dsmack=true ++ -Dgcrypt=true ++ -Daudit=true ++ -Delfutils=true ++ -Dlibcryptsetup=true ++ -Delfutils=true ++ -Dqrencode=false ++ -Dgnutls=true ++ -Dmicrohttpd=true ++ -Dlibidn2=true ++ -Dlibiptc=true ++ -Dlibcurl=true ++ -Defi=true ++ -Dtpm=true ++ -Dhwdb=true ++ -Dsysusers=true ++ -Ddefault-kill-user-processes=false ++ -Dtests=unsafe ++ -Dinstall-tests=true ++ -Dtty-gid=5 ++ -Dusers-gid=100 ++ -Dnobody-user=nobody ++ -Dnobody-group=nobody ++ -Dsplit-usr=false ++ -Dsplit-bin=true ++ -Db_lto=false ++ -Dnetworkd=false ++ -Dtimesyncd=false ++ -Ddefault-hierarchy=legacy ++ # Custom options ++ -Dslow-tests=true ++ -Dtests=unsafe ++ -Dinstall-tests=true ++ ) ++ docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build "${CONFIGURE_OPTS[@]}" ++ $DOCKER_EXEC ninja -v -C build ++ # "Mask" the udev-test.pl, as it requires newer version of systemd-detect-virt ++ # and it's pointless to run it on a VM in a Docker container... ++ echo -ne "#!/usr/bin/perl\nexit(0);\n" > "test/udev-test.pl" ++ $DOCKER_EXEC ninja -C build test ++ ;; ++ CLEANUP) ++ info "Cleanup phase" ++ docker stop $CONT_NAME ++ docker rm -f $CONT_NAME ++ ;; ++ *) ++ echo >&2 "Unknown phase '$phase'" ++ exit 1 ++ esac ++done diff --git a/SOURCES/0065-travis-drop-the-SELinux-Fedora-workaround.patch b/SOURCES/0065-travis-drop-the-SELinux-Fedora-workaround.patch new file mode 100644 index 0000000..de14c0a --- /dev/null +++ b/SOURCES/0065-travis-drop-the-SELinux-Fedora-workaround.patch @@ -0,0 +1,37 @@ +From 2d674d48e9ca48e3bb126f20b59334100d926a23 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 15 Jan 2019 11:03:45 +0100 +Subject: [PATCH] travis: drop the SELinux Fedora workaround + +(cherry picked from commit 90399c456fe8cf726fc04fb7be9e2a01f9ca0eae) +--- + ci/travis-centos-rhel7.sh | 2 +- + ci/travis-centos-rhel8.sh | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ci/travis-centos-rhel7.sh b/ci/travis-centos-rhel7.sh +index 60bbdf14c2..b1b3de1cc2 100755 +--- a/ci/travis-centos-rhel7.sh ++++ b/ci/travis-centos-rhel7.sh +@@ -40,7 +40,7 @@ for phase in "${PHASES[@]}"; do + sleep 1 + $DOCKER_EXEC yum makecache + # Install necessary build/test requirements +- $DOCKER_EXEC yum -y --exclude selinux-policy\* upgrade ++ $DOCKER_EXEC yum -y upgrade + $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" + $DOCKER_EXEC yum-builddep -y systemd + ;; +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index 968603f949..8eda5e982f 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -45,7 +45,7 @@ for phase in "${PHASES[@]}"; do + $DOCKER_EXEC curl "$COPR_REPO" -o "$COPR_REPO_PATH" + $DOCKER_EXEC yum -q -y install epel-release yum-utils + $DOCKER_EXEC yum-config-manager -q --enable epel +- $DOCKER_EXEC yum -y --exclude selinux-policy\* upgrade ++ $DOCKER_EXEC yum -y upgrade + # Install necessary build/test requirements + $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" + $DOCKER_EXEC python3.6 -m ensurepip diff --git a/SOURCES/0066-travis-fix-syntax-error-in-.travis.yml.patch b/SOURCES/0066-travis-fix-syntax-error-in-.travis.yml.patch new file mode 100644 index 0000000..d26bbfb --- /dev/null +++ b/SOURCES/0066-travis-fix-syntax-error-in-.travis.yml.patch @@ -0,0 +1,24 @@ +From a7f87d13f6f7dd92e1f1f7617df531fa34c70b6d Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 15 Jan 2019 14:35:27 +0100 +Subject: [PATCH] travis: fix syntax error in .travis.yml + +(cherry picked from commit 7f9d44f527ea214347f7d3b3b067f84df53feed7) +--- + .travis.yml | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 1c4e6f9728..c5c9c345a9 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -19,8 +19,7 @@ jobs: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version + install: +- - RHEL_VERSION="rhel7" +- - [ -f meson.build ] && RHEL_VERSION="rhel8" ++ - if [ -f meson.build ]; then RHEL_VERSION=rhel8; else RHEL_VERSION=rhel7; fi + - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh SETUP + script: + - set -e diff --git a/SOURCES/0067-travis-reboot-the-container-before-running-tests.patch b/SOURCES/0067-travis-reboot-the-container-before-running-tests.patch new file mode 100644 index 0000000..5f59065 --- /dev/null +++ b/SOURCES/0067-travis-reboot-the-container-before-running-tests.patch @@ -0,0 +1,40 @@ +From 63e71bda5a00c04c16f330cfc0e6f91e7dcead59 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Thu, 17 Jan 2019 12:03:10 +0100 +Subject: [PATCH] travis: reboot the container before running tests + +--- + ci/travis-centos-rhel7.sh | 4 ++++ + ci/travis-centos-rhel8.sh | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/ci/travis-centos-rhel7.sh b/ci/travis-centos-rhel7.sh +index b1b3de1cc2..73416798ed 100755 +--- a/ci/travis-centos-rhel7.sh ++++ b/ci/travis-centos-rhel7.sh +@@ -52,6 +52,10 @@ for phase in "${PHASES[@]}"; do + --enable-gtk-doc --enable-compat-libs --disable-sysusers \ + --disable-ldconfig --enable-lz4 --with-sysvinit-path=/etc/rc.d/init.d + $DOCKER_EXEC make ++ # Let's install the new systemd and "reboot" the container to avoid ++ # unexpected fails due to incompatibilities with older systemd ++ $DOCKER_EXEC make install ++ docker restart $CONT_NAME + if ! $DOCKER_EXEC make check; then + $DOCKER_EXEC cat test-suite.log + exit 1 +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index 8eda5e982f..1f72d984e0 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -113,6 +113,10 @@ for phase in "${PHASES[@]}"; do + ) + docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build "${CONFIGURE_OPTS[@]}" + $DOCKER_EXEC ninja -v -C build ++ # Let's install the new systemd and "reboot" the container to avoid ++ # unexpected fails due to incompatibilities with older systemd ++ $DOCKER_EXEC ninja -C build install ++ docker restart $CONT_NAME + # "Mask" the udev-test.pl, as it requires newer version of systemd-detect-virt + # and it's pointless to run it on a VM in a Docker container... + echo -ne "#!/usr/bin/perl\nexit(0);\n" > "test/udev-test.pl" diff --git a/SOURCES/0068-coredump-remove-duplicate-MESSAGE-prefix-from-messag.patch b/SOURCES/0068-coredump-remove-duplicate-MESSAGE-prefix-from-messag.patch new file mode 100644 index 0000000..5c2ff84 --- /dev/null +++ b/SOURCES/0068-coredump-remove-duplicate-MESSAGE-prefix-from-messag.patch @@ -0,0 +1,35 @@ +From af43906bb0c8f2bb3b135d68d56ea2fa58fa9e60 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 17:33:15 +0100 +Subject: [PATCH] coredump: remove duplicate MESSAGE= prefix from message + +systemd-coredump[9982]: MESSAGE=Process 771 (systemd-journal) of user 0 dumped core. +systemd-coredump[9982]: Coredump diverted to /var/lib/systemd/coredump/core... + +log_dispatch() calls log_dispatch_internal() which calls write_to_journal() +which appends MESSAGE= on its own. + +(cherry-picked from commit 4f62556d71206ac814a020a954b397d4940e14c3) + +Related: #1664976 +--- + src/coredump/coredump.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c +index 20a1cbdd45..d7dd81c1b4 100644 +--- a/src/coredump/coredump.c ++++ b/src/coredump/coredump.c +@@ -789,9 +789,10 @@ log: + return log_oom(); + + if (journald_crash) { +- /* We cannot log to the journal, so just print the MESSAGE. ++ /* We cannot log to the journal, so just print the message. + * The target was set previously to something safe. */ +- log_dispatch(LOG_ERR, 0, core_message); ++ assert(startswith(core_message, "MESSAGE=")); ++ log_dispatch(LOG_ERR, 0, core_message + strlen("MESSAGE=")); + return 0; + } + diff --git a/SOURCES/0069-journald-remove-unnecessary.patch b/SOURCES/0069-journald-remove-unnecessary.patch new file mode 100644 index 0000000..aaffa53 --- /dev/null +++ b/SOURCES/0069-journald-remove-unnecessary.patch @@ -0,0 +1,34 @@ +From 285e4d2ce6a8836ce7bf2e889d43b7272f7ccc1b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 17:53:50 +0100 +Subject: [PATCH] journald: remove unnecessary {} + +(cherry-picked from commit bc2762a309132a34db1797d8b5792d5747a94484) + +Related: #1664976 +--- + src/journal/journald-server.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 4f1550ec5b..ce2446a2f1 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -1124,8 +1124,7 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void + return log_error_errno(errno, "recvmsg() failed: %m"); + } + +- CMSG_FOREACH(cmsg, &msghdr) { +- ++ CMSG_FOREACH(cmsg, &msghdr) + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) +@@ -1143,7 +1142,6 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } +- } + + /* And a trailing NUL, just in case */ + s->buffer[n] = 0; diff --git a/SOURCES/0070-journald-do-not-store-the-iovec-entry-for-process-co.patch b/SOURCES/0070-journald-do-not-store-the-iovec-entry-for-process-co.patch new file mode 100644 index 0000000..40af46e --- /dev/null +++ b/SOURCES/0070-journald-do-not-store-the-iovec-entry-for-process-co.patch @@ -0,0 +1,202 @@ +From b6c10945e68949edc6418f48ca7b1b748fefabe1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 18:38:39 +0100 +Subject: [PATCH] journald: do not store the iovec entry for process + commandline on stack + +This fixes a crash where we would read the commandline, whose length is under +control of the sending program, and then crash when trying to create a stack +allocation for it. + +CVE-2018-16864 +https://bugzilla.redhat.com/show_bug.cgi?id=1653855 + +The message actually doesn't get written to disk, because +journal_file_append_entry() returns -E2BIG. + +(cherry-picked from commit 084eeb865ca63887098e0945fb4e93c852b91b0f) + +Resolves: #1664976 +--- + src/basic/io-util.c | 10 ++++++++++ + src/basic/io-util.h | 2 ++ + src/coredump/coredump.c | 31 +++++++++++-------------------- + src/journal/journald-server.c | 25 +++++++++++++++---------- + 4 files changed, 38 insertions(+), 30 deletions(-) + +diff --git a/src/basic/io-util.c b/src/basic/io-util.c +index 1f64cc933b..575398fbe6 100644 +--- a/src/basic/io-util.c ++++ b/src/basic/io-util.c +@@ -8,6 +8,7 @@ + #include + + #include "io-util.h" ++#include "string-util.h" + #include "time-util.h" + + int flush_fd(int fd) { +@@ -252,3 +253,12 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) { + + return q - (const uint8_t*) p; + } ++ ++char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) { ++ char *x; ++ ++ x = strappend(field, value); ++ if (x) ++ iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x); ++ return x; ++} +diff --git a/src/basic/io-util.h b/src/basic/io-util.h +index ed189b5820..792a64ad5e 100644 +--- a/src/basic/io-util.h ++++ b/src/basic/io-util.h +@@ -71,3 +71,5 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { + #define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len) + #define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string)) + #define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string) ++ ++char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); +diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c +index d7dd81c1b4..ffa88f612d 100644 +--- a/src/coredump/coredump.c ++++ b/src/coredump/coredump.c +@@ -1054,19 +1054,10 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) + return 0; + } + +-static char* set_iovec_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) { +- char *x; +- +- x = strappend(field, value); +- if (x) +- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x); +- return x; +-} +- + static char* set_iovec_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) { + char *x; + +- x = set_iovec_field(iovec, n_iovec, field, value); ++ x = set_iovec_string_field(iovec, n_iovec, field, value); + free(value); + return x; + } +@@ -1116,36 +1107,36 @@ static int gather_pid_metadata( + disable_coredumps(); + } + +- set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]); ++ set_iovec_string_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]); + } + + if (cg_pid_get_user_unit(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t); + + /* The next few are mandatory */ +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID])) + return log_oom(); + +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID])) + return log_oom(); + +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID])) + return log_oom(); + +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL])) + return log_oom(); + +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT])) + return log_oom(); + +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME])) + return log_oom(); + +- if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM])) ++ if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM])) + return log_oom(); + + if (context[CONTEXT_EXE] && +- !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE])) ++ !set_iovec_string_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE])) + return log_oom(); + + if (sd_pid_get_session(pid, &t) >= 0) +@@ -1213,7 +1204,7 @@ static int gather_pid_metadata( + iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t); + + if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo)) +- set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo)); ++ set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo)); + + return 0; /* we successfully acquired all metadata */ + } +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index ce2446a2f1..8de45552f6 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -753,6 +753,7 @@ static void dispatch_message_real( + pid_t object_pid) { + + char source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)]; ++ _cleanup_free_ char *cmdline1 = NULL, *cmdline2 = NULL; + uid_t journal_uid; + ClientContext *o; + +@@ -769,20 +770,23 @@ static void dispatch_message_real( + IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->uid, uid_t, uid_is_valid, UID_FMT, "_UID"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->gid, gid_t, gid_is_valid, GID_FMT, "_GID"); + +- IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM"); +- IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE"); +- IOVEC_ADD_STRING_FIELD(iovec, n, c->cmdline, "_CMDLINE"); +- IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); ++ IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM"); /* At most TASK_COMM_LENGTH (16 bytes) */ ++ IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE"); /* A path, so at most PATH_MAX (4096 bytes) */ + +- IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT"); ++ if (c->cmdline) ++ /* At most _SC_ARG_MAX (2MB usually), which is too much to put on stack. ++ * Let's use a heap allocation for this one. */ ++ cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline); + ++ IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */ ++ IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID"); + +- IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP"); ++ IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP"); /* A path */ + IOVEC_ADD_STRING_FIELD(iovec, n, c->session, "_SYSTEMD_SESSION"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->owner_uid, uid_t, uid_is_valid, UID_FMT, "_SYSTEMD_OWNER_UID"); +- IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT"); ++ IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT"); /* Unit names are bounded by UNIT_NAME_MAX */ + IOVEC_ADD_STRING_FIELD(iovec, n, c->user_unit, "_SYSTEMD_USER_UNIT"); + IOVEC_ADD_STRING_FIELD(iovec, n, c->slice, "_SYSTEMD_SLICE"); + IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE"); +@@ -803,13 +807,14 @@ static void dispatch_message_real( + IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->uid, uid_t, uid_is_valid, UID_FMT, "OBJECT_UID"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->gid, gid_t, gid_is_valid, GID_FMT, "OBJECT_GID"); + ++ /* See above for size limits, only ->cmdline may be large, so use a heap allocation for it. */ + IOVEC_ADD_STRING_FIELD(iovec, n, o->comm, "OBJECT_COMM"); + IOVEC_ADD_STRING_FIELD(iovec, n, o->exe, "OBJECT_EXE"); +- IOVEC_ADD_STRING_FIELD(iovec, n, o->cmdline, "OBJECT_CMDLINE"); +- IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE"); ++ if (o->cmdline) ++ cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline); + ++ IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE"); + IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT"); +- + IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID"); + diff --git a/SOURCES/0071-basic-process-util-limit-command-line-lengths-to-_SC.patch b/SOURCES/0071-basic-process-util-limit-command-line-lengths-to-_SC.patch new file mode 100644 index 0000000..f1e9a62 --- /dev/null +++ b/SOURCES/0071-basic-process-util-limit-command-line-lengths-to-_SC.patch @@ -0,0 +1,159 @@ +From 6298317e2d0dffb1ff4ecebedb8709645de36b6a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 18:48:23 +0100 +Subject: [PATCH] basic/process-util: limit command line lengths to _SC_ARG_MAX + +This affects systemd-journald and systemd-coredump. + +Example entry: +$ journalctl -o export -n1 'MESSAGE=Something logged' +__CURSOR=s=976542d120c649f494471be317829ef9;i=34e;b=4871e4c474574ce4a462dfe3f1c37f06;m=c7d0c37dd2;t=57c4ac58f3b98;x=67598e942bd23dc0 +__REALTIME_TIMESTAMP=1544035467475864 +__MONOTONIC_TIMESTAMP=858200964562 +_BOOT_ID=4871e4c474574ce4a462dfe3f1c37f06 +PRIORITY=6 +_UID=1000 +_GID=1000 +_CAP_EFFECTIVE=0 +_SELINUX_CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 +_AUDIT_SESSION=1 +_AUDIT_LOGINUID=1000 +_SYSTEMD_OWNER_UID=1000 +_SYSTEMD_UNIT=user@1000.service +_SYSTEMD_SLICE=user-1000.slice +_SYSTEMD_USER_SLICE=-.slice +_SYSTEMD_INVOCATION_ID=1c4a469986d448719cb0f9141a10810e +_MACHINE_ID=08a5690a2eed47cf92ac0a5d2e3cf6b0 +_HOSTNAME=krowka +_TRANSPORT=syslog +SYSLOG_FACILITY=17 +SYSLOG_IDENTIFIER=syslog-caller +MESSAGE=Something logged +_COMM=poc +_EXE=/home/zbyszek/src/systemd-work3/poc +_SYSTEMD_CGROUP=/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service +_SYSTEMD_USER_UNIT=gnome-terminal-server.service +SYSLOG_PID=4108 +SYSLOG_TIMESTAMP=Dec 5 19:44:27 +_PID=4108 +_CMDLINE=./poc AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA> +_SOURCE_REALTIME_TIMESTAMP=1544035467475848 + +$ journalctl -o export -n1 'MESSAGE=Something logged' --output-fields=_CMDLINE|wc + 6 2053 2097410 + +2MB might be hard for some clients to use meaningfully, but OTOH, it is +important to log the full commandline sometimes. For example, when the program +is crashing, the exact argument list is useful. + +(cherry-picked from commit 2d5d2e0cc5171c6795d2a485841474345d9e30ab) + +Related: #1664976 +--- + src/basic/process-util.c | 73 ++++++++++++++-------------------------- + 1 file changed, 25 insertions(+), 48 deletions(-) + +diff --git a/src/basic/process-util.c b/src/basic/process-util.c +index 0a4f917cbd..a20f1e3ccf 100644 +--- a/src/basic/process-util.c ++++ b/src/basic/process-util.c +@@ -128,6 +128,13 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + ++ if (max_length == 0) { ++ /* This is supposed to be a safety guard against runaway command lines. */ ++ long l = sysconf(_SC_ARG_MAX); ++ assert(l > 0); ++ max_length = l; ++ } ++ + if (max_length == 1) { + + /* If there's only room for one byte, return the empty string */ +@@ -138,32 +145,6 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + *line = ans; + return 0; + +- } else if (max_length == 0) { +- size_t len = 0, allocated = 0; +- +- while ((c = getc(f)) != EOF) { +- +- if (!GREEDY_REALLOC(ans, allocated, len+3)) { +- free(ans); +- return -ENOMEM; +- } +- +- if (isprint(c)) { +- if (space) { +- ans[len++] = ' '; +- space = false; +- } +- +- ans[len++] = c; +- } else if (len > 0) +- space = true; +- } +- +- if (len > 0) +- ans[len] = '\0'; +- else +- ans = mfree(ans); +- + } else { + bool dotdotdot = false; + size_t left; +@@ -235,34 +216,30 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + if (h < 0) + return h; + +- if (max_length == 0) +- ans = strjoin("[", t, "]"); +- else { +- size_t l; +- +- l = strlen(t); ++ size_t l = strlen(t); + +- if (l + 3 <= max_length) +- ans = strjoin("[", t, "]"); +- else if (max_length <= 6) { ++ if (l + 3 <= max_length) { ++ ans = strjoin("[", t, "]"); ++ if (!ans) ++ return -ENOMEM; + +- ans = new(char, max_length); +- if (!ans) +- return -ENOMEM; ++ } else if (max_length <= 6) { ++ ans = new(char, max_length); ++ if (!ans) ++ return -ENOMEM; + +- memcpy(ans, "[...]", max_length-1); +- ans[max_length-1] = 0; +- } else { +- t[max_length - 6] = 0; ++ memcpy(ans, "[...]", max_length-1); ++ ans[max_length-1] = 0; ++ } else { ++ t[max_length - 6] = 0; + +- /* Chop off final spaces */ +- delete_trailing_chars(t, WHITESPACE); ++ /* Chop off final spaces */ ++ delete_trailing_chars(t, WHITESPACE); + +- ans = strjoin("[", t, "...]"); +- } ++ ans = strjoin("[", t, "...]"); ++ if (!ans) ++ return -ENOMEM; + } +- if (!ans) +- return -ENOMEM; + } + + *line = ans; diff --git a/SOURCES/0072-coredump-fix-message-when-we-fail-to-save-a-journald.patch b/SOURCES/0072-coredump-fix-message-when-we-fail-to-save-a-journald.patch new file mode 100644 index 0000000..0041902 --- /dev/null +++ b/SOURCES/0072-coredump-fix-message-when-we-fail-to-save-a-journald.patch @@ -0,0 +1,33 @@ +From 04326c02ca80666e66a5da8e6a46c2fc4476000c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 21:34:24 +0100 +Subject: [PATCH] coredump: fix message when we fail to save a journald + coredump + +If creation of the message failed, we'd write a bogus entry: +systemd-coredump[1400]: Cannot store coredump of 416 (systemd-journal): No space left on device +systemd-coredump[1400]: MESSAGE=Process 416 (systemd-journal) of user 0 dumped core. +systemd-coredump[1400]: Coredump diverted to + +(cherry-picked from commit f0136e09221364f931c3a3b715da4e4d3ee9f2ac) + +Related: #1664976 +--- + src/coredump/coredump.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c +index ffa88f612d..2a130e8838 100644 +--- a/src/coredump/coredump.c ++++ b/src/coredump/coredump.c +@@ -783,8 +783,8 @@ log: + core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], + " (", context[CONTEXT_COMM], ") of user ", + context[CONTEXT_UID], " dumped core.", +- journald_crash ? "\nCoredump diverted to " : NULL, +- journald_crash ? filename : NULL); ++ journald_crash && filename ? "\nCoredump diverted to " : NULL, ++ journald_crash && filename ? filename : NULL); + if (!core_message) + return log_oom(); + diff --git a/SOURCES/0073-procfs-util-expose-functionality-to-query-total-memo.patch b/SOURCES/0073-procfs-util-expose-functionality-to-query-total-memo.patch new file mode 100644 index 0000000..8d9285f --- /dev/null +++ b/SOURCES/0073-procfs-util-expose-functionality-to-query-total-memo.patch @@ -0,0 +1,104 @@ +From 60b831ef50e435b66ddd99e635a5112e121c7cb3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 22 Jan 2019 15:43:07 +0100 +Subject: [PATCH] procfs-util: expose functionality to query total memory + +procfs_memory_get_current is renamed to procfs_memory_get_used, because +"current" can mean anything, including total memory, used memory, and free +memory, as long as the value is up to date. + +No functional change. + +(cherry-picked from commit c482724aa5c5d0b1391fcf958a9a3ea6ce73a085) + +Related: #1664976 +--- + src/basic/procfs-util.c | 9 +++++---- + src/basic/procfs-util.h | 5 ++++- + src/cgtop/cgtop.c | 2 +- + src/core/cgroup.c | 2 +- + src/test/test-procfs-util.c | 2 +- + 5 files changed, 12 insertions(+), 8 deletions(-) + +diff --git a/src/basic/procfs-util.c b/src/basic/procfs-util.c +index a159e344b3..7aaf95bfce 100644 +--- a/src/basic/procfs-util.c ++++ b/src/basic/procfs-util.c +@@ -201,13 +201,11 @@ int procfs_cpu_get_usage(nsec_t *ret) { + return 0; + } + +-int procfs_memory_get_current(uint64_t *ret) { ++int procfs_memory_get(uint64_t *ret_total, uint64_t *ret_used) { + uint64_t mem_total = UINT64_MAX, mem_free = UINT64_MAX; + _cleanup_fclose_ FILE *f = NULL; + int r; + +- assert(ret); +- + f = fopen("/proc/meminfo", "re"); + if (!f) + return -errno; +@@ -262,6 +260,9 @@ int procfs_memory_get_current(uint64_t *ret) { + if (mem_free > mem_total) + return -EINVAL; + +- *ret = (mem_total - mem_free) * 1024U; ++ if (ret_total) ++ *ret_total = mem_total * 1024U; ++ if (ret_used) ++ *ret_used = (mem_total - mem_free) * 1024U; + return 0; + } +diff --git a/src/basic/procfs-util.h b/src/basic/procfs-util.h +index f697ed92bc..5a44e9eff7 100644 +--- a/src/basic/procfs-util.h ++++ b/src/basic/procfs-util.h +@@ -11,4 +11,7 @@ int procfs_tasks_get_current(uint64_t *ret); + + int procfs_cpu_get_usage(nsec_t *ret); + +-int procfs_memory_get_current(uint64_t *ret); ++int procfs_memory_get(uint64_t *ret_total, uint64_t *ret_used); ++static inline int procfs_memory_get_used(uint64_t *ret) { ++ return procfs_memory_get(NULL, ret); ++} +diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c +index 8dda08ab4c..792b13a43d 100644 +--- a/src/cgtop/cgtop.c ++++ b/src/cgtop/cgtop.c +@@ -297,7 +297,7 @@ static int process( + } else if (streq(controller, "memory")) { + + if (is_root_cgroup(path)) { +- r = procfs_memory_get_current(&g->memory); ++ r = procfs_memory_get_used(&g->memory); + if (r < 0) + return r; + } else { +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index bb02436203..62ab41a288 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -2402,7 +2402,7 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { + + /* The root cgroup doesn't expose this information, let's get it from /proc instead */ + if (unit_has_root_cgroup(u)) +- return procfs_memory_get_current(ret); ++ return procfs_memory_get_used(ret); + + if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0) + return -ENODATA; +diff --git a/src/test/test-procfs-util.c b/src/test/test-procfs-util.c +index 08af380cc7..1d0612985b 100644 +--- a/src/test/test-procfs-util.c ++++ b/src/test/test-procfs-util.c +@@ -18,7 +18,7 @@ int main(int argc, char *argv[]) { + assert_se(procfs_cpu_get_usage(&nsec) >= 0); + log_info("Current system CPU time: %s", format_timespan(buf, sizeof(buf), nsec/NSEC_PER_USEC, 1)); + +- assert_se(procfs_memory_get_current(&v) >= 0); ++ assert_se(procfs_memory_get_used(&v) >= 0); + log_info("Current memory usage: %s", format_bytes(buf, sizeof(buf), v)); + + assert_se(procfs_tasks_get_current(&v) >= 0); diff --git a/SOURCES/0074-basic-prioq-add-prioq_peek_item.patch b/SOURCES/0074-basic-prioq-add-prioq_peek_item.patch new file mode 100644 index 0000000..828bff6 --- /dev/null +++ b/SOURCES/0074-basic-prioq-add-prioq_peek_item.patch @@ -0,0 +1,114 @@ +From ee14a2bd3d95b5d15e4d72ee2582b366e5009a86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 26 Jan 2019 11:27:18 +0100 +Subject: [PATCH] basic/prioq: add prioq_peek_item() + +(cherry-picked from commit ef21b3b5bf824e652addf850bcfd9374c7b33ce8) + +Related: #1664976 +--- + src/basic/prioq.c | 7 +++---- + src/basic/prioq.h | 8 +++++++- + src/test/test-prioq.c | 23 +++++++++++++++++------ + 3 files changed, 27 insertions(+), 11 deletions(-) + +diff --git a/src/basic/prioq.c b/src/basic/prioq.c +index ef28a086d1..0bf58c1f16 100644 +--- a/src/basic/prioq.c ++++ b/src/basic/prioq.c +@@ -259,15 +259,14 @@ int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { + return 1; + } + +-void *prioq_peek(Prioq *q) { +- ++void *prioq_peek_by_index(Prioq *q, unsigned idx) { + if (!q) + return NULL; + +- if (q->n_items <= 0) ++ if (idx >= q->n_items) + return NULL; + +- return q->items[0].data; ++ return q->items[idx].data; + } + + void *prioq_pop(Prioq *q) { +diff --git a/src/basic/prioq.h b/src/basic/prioq.h +index e036175260..c381523525 100644 +--- a/src/basic/prioq.h ++++ b/src/basic/prioq.h +@@ -18,8 +18,14 @@ int prioq_put(Prioq *q, void *data, unsigned *idx); + int prioq_remove(Prioq *q, void *data, unsigned *idx); + int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); + +-void *prioq_peek(Prioq *q) _pure_; ++void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_; ++static inline void *prioq_peek(Prioq *q) { ++ return prioq_peek_by_index(q, 0); ++} + void *prioq_pop(Prioq *q); + ++#define PRIOQ_FOREACH_ITEM(q, p) \ ++ for (unsigned _i = 0; (p = prioq_peek_by_index(q, _i)); _i++) ++ + unsigned prioq_size(Prioq *q) _pure_; + bool prioq_isempty(Prioq *q) _pure_; +diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c +index 89c41d8ce7..ece13808ed 100644 +--- a/src/test/test-prioq.c ++++ b/src/test/test-prioq.c +@@ -87,6 +87,7 @@ static void test_struct(void) { + Set *s; + unsigned previous = 0, i; + int r; ++ struct test *t; + + srand(0); + +@@ -96,9 +97,12 @@ static void test_struct(void) { + s = set_new(&test_hash_ops); + assert_se(s); + +- for (i = 0; i < SET_SIZE; i++) { +- struct test *t; ++ assert_se(prioq_peek(q) == NULL); ++ assert_se(prioq_peek_by_index(q, 0) == NULL); ++ assert_se(prioq_peek_by_index(q, 1) == NULL); ++ assert_se(prioq_peek_by_index(q, (unsigned) -1) == NULL); + ++ for (i = 0; i < SET_SIZE; i++) { + t = new0(struct test, 1); + assert_se(t); + t->value = (unsigned) rand(); +@@ -112,9 +116,18 @@ static void test_struct(void) { + } + } + +- for (;;) { +- struct test *t; ++ for (i = 0; i < SET_SIZE; i++) ++ assert_se(prioq_peek_by_index(q, i)); ++ assert_se(prioq_peek_by_index(q, SET_SIZE) == NULL); ++ ++ unsigned count = 0; ++ PRIOQ_FOREACH_ITEM(q, t) { ++ assert_se(t); ++ count++; ++ } ++ assert_se(count == SET_SIZE); + ++ for (;;) { + t = set_steal_first(s); + if (!t) + break; +@@ -126,8 +139,6 @@ static void test_struct(void) { + } + + for (i = 0; i < SET_SIZE * 3 / 4; i++) { +- struct test *t; +- + assert_se(prioq_size(q) == (SET_SIZE * 3 / 4) - i); + + t = prioq_pop(q); diff --git a/SOURCES/0075-journal-limit-the-number-of-entries-in-the-cache-bas.patch b/SOURCES/0075-journal-limit-the-number-of-entries-in-the-cache-bas.patch new file mode 100644 index 0000000..d2bcd3b --- /dev/null +++ b/SOURCES/0075-journal-limit-the-number-of-entries-in-the-cache-bas.patch @@ -0,0 +1,81 @@ +From de72fa6b0582b95216215cc1400412fe91bc8ba3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 22 Jan 2019 16:12:52 +0100 +Subject: [PATCH] journal: limit the number of entries in the cache based on + available memory + +This is far from perfect, but should give mostly reasonable values. My +assumption is that if somebody has a few hundred MB of memory, they are +unlikely to have thousands of processes logging. A hundred would already be a +lot. So let's scale the cache size propritionally to the total memory size, +with clamping on both ends. + +The formula gives 64 cache entries for each GB of RAM. + +(cherry-picked from commit b12a480829c5ca8f4d4fa9cde8716b5f2f12a3ad) + +Related: #1664976 +--- + src/journal/journald-context.c | 35 ++++++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c +index ce07de1bfb..0f0dc1de4d 100644 +--- a/src/journal/journald-context.c ++++ b/src/journal/journald-context.c +@@ -14,6 +14,7 @@ + #include "journal-util.h" + #include "journald-context.h" + #include "process-util.h" ++#include "procfs-util.h" + #include "string-util.h" + #include "syslog-util.h" + #include "unaligned.h" +@@ -58,7 +59,37 @@ + /* Keep at most 16K entries in the cache. (Note though that this limit may be violated if enough streams pin entries in + * the cache, in which case we *do* permit this limit to be breached. That's safe however, as the number of stream + * clients itself is limited.) */ +-#define CACHE_MAX (16*1024) ++#define CACHE_MAX_FALLBACK 128U ++#define CACHE_MAX_MAX (16*1024U) ++#define CACHE_MAX_MIN 64U ++ ++static size_t cache_max(void) { ++ static size_t cached = -1; ++ ++ if (cached == (size_t) -1) { ++ uint64_t mem_total; ++ int r; ++ ++ r = procfs_memory_get(&mem_total, NULL); ++ if (r < 0) { ++ log_warning_errno(r, "Cannot query /proc/meminfo for MemTotal: %m"); ++ cached = CACHE_MAX_FALLBACK; ++ } else { ++ /* Cache entries are usually a few kB, but the process cmdline is controlled by the ++ * user and can be up to _SC_ARG_MAX, usually 2MB. Let's say that approximately up to ++ * 1/8th of memory may be used by the cache. ++ * ++ * In the common case, this formula gives 64 cache entries for each GB of RAM. ++ */ ++ long l = sysconf(_SC_ARG_MAX); ++ assert(l > 0); ++ ++ cached = CLAMP(mem_total / 8 / (uint64_t) l, CACHE_MAX_MIN, CACHE_MAX_MAX); ++ } ++ } ++ ++ return cached; ++} + + static int client_context_compare(const void *a, const void *b) { + const ClientContext *x = a, *y = b; +@@ -587,7 +618,7 @@ static int client_context_get_internal( + return 0; + } + +- client_context_try_shrink_to(s, CACHE_MAX-1); ++ client_context_try_shrink_to(s, cache_max()-1); + + r = client_context_new(s, pid, &c); + if (r < 0) diff --git a/SOURCES/0076-journald-periodically-drop-cache-for-all-dead-PIDs.patch b/SOURCES/0076-journald-periodically-drop-cache-for-all-dead-PIDs.patch new file mode 100644 index 0000000..cad2d3e --- /dev/null +++ b/SOURCES/0076-journald-periodically-drop-cache-for-all-dead-PIDs.patch @@ -0,0 +1,77 @@ +From 8da81d2aba2768ced497790cc05b9f73c6268833 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 22 Jan 2019 17:30:48 +0100 +Subject: [PATCH] journald: periodically drop cache for all dead PIDs + +In normal use, this allow us to drop dead entries from the cache and reduces +the cache size so that we don't evict entries unnecessarily. The time limit is +there mostly to serve as a guard against malicious logging from many different +PIDs. + +(cherry-picked from commit 91714a7f427a6c9c5c3be8b3819fee45050028f3) + +Related: #1664976 +--- + src/journal/journald-context.c | 28 ++++++++++++++++++++++++++-- + src/journal/journald-server.h | 2 ++ + 2 files changed, 28 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c +index 0f0dc1de4d..51f79fd803 100644 +--- a/src/journal/journald-context.c ++++ b/src/journal/journald-context.c +@@ -541,15 +541,39 @@ refresh: + } + + static void client_context_try_shrink_to(Server *s, size_t limit) { ++ ClientContext *c; ++ usec_t t; ++ + assert(s); + ++ /* Flush any cache entries for PIDs that have already moved on. Don't do this ++ * too often, since it's a slow process. */ ++ t = now(CLOCK_MONOTONIC); ++ if (s->last_cache_pid_flush + MAX_USEC < t) { ++ unsigned n = prioq_size(s->client_contexts_lru), idx = 0; ++ ++ /* We do a number of iterations based on the initial size of the prioq. When we remove an ++ * item, a new item is moved into its places, and items to the right might be reshuffled. ++ */ ++ for (unsigned i = 0; i < n; i++) { ++ c = prioq_peek_by_index(s->client_contexts_lru, idx); ++ ++ assert(c->n_ref == 0); ++ ++ if (!pid_is_unwaited(c->pid)) ++ client_context_free(s, c); ++ else ++ idx ++; ++ } ++ ++ s->last_cache_pid_flush = t; ++ } ++ + /* Bring the number of cache entries below the indicated limit, so that we can create a new entry without + * breaching the limit. Note that we only flush out entries that aren't pinned here. This means the number of + * cache entries may very well grow beyond the limit, if all entries stored remain pinned. */ + + while (hashmap_size(s->client_contexts) > limit) { +- ClientContext *c; +- + c = prioq_pop(s->client_contexts_lru); + if (!c) + break; /* All remaining entries are pinned, give up */ +diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h +index 983be8bb89..c6c9b1fb1d 100644 +--- a/src/journal/journald-server.h ++++ b/src/journal/journald-server.h +@@ -163,6 +163,8 @@ struct Server { + Hashmap *client_contexts; + Prioq *client_contexts_lru; + ++ usec_t last_cache_pid_flush; ++ + ClientContext *my_context; /* the context of journald itself */ + ClientContext *pid1_context; /* the context of PID 1 */ + }; diff --git a/SOURCES/0077-process-util-don-t-use-overly-large-buffer-to-store-.patch b/SOURCES/0077-process-util-don-t-use-overly-large-buffer-to-store-.patch new file mode 100644 index 0000000..3e583ff --- /dev/null +++ b/SOURCES/0077-process-util-don-t-use-overly-large-buffer-to-store-.patch @@ -0,0 +1,71 @@ +From 9b9b6d8c7b10c069d36f85bd17f144011282cb58 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 22 Jan 2019 14:29:50 +0100 +Subject: [PATCH] process-util: don't use overly large buffer to store process + command line + +Allocate new string as a return value and free our "scratch pad" +buffer that is potentially much larger than needed (up to +_SC_ARG_MAX). + +Fixes #11502 + +(cherry-picked from commit eb1ec489eef8a32918bbfc56a268c9d10464584d) + +Related: #1664976 +--- + src/basic/process-util.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/src/basic/process-util.c b/src/basic/process-util.c +index a20f1e3ccf..aa3eff779a 100644 +--- a/src/basic/process-util.c ++++ b/src/basic/process-util.c +@@ -101,7 +101,8 @@ int get_process_comm(pid_t pid, char **ret) { + int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + _cleanup_fclose_ FILE *f = NULL; + bool space = false; +- char *k, *ans = NULL; ++ char *k; ++ _cleanup_free_ char *ans = NULL; + const char *p; + int c; + +@@ -142,7 +143,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + if (!ans) + return -ENOMEM; + +- *line = ans; ++ *line = TAKE_PTR(ans); + return 0; + + } else { +@@ -207,7 +208,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + _cleanup_free_ char *t = NULL; + int h; + +- free(ans); ++ ans = mfree(ans); + + if (!comm_fallback) + return -ENOENT; +@@ -240,9 +241,18 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + if (!ans) + return -ENOMEM; + } ++ ++ *line = TAKE_PTR(ans); ++ return 0; + } + +- *line = ans; ++ k = realloc(ans, strlen(ans) + 1); ++ if (!k) ++ return -ENOMEM; ++ ++ ans = NULL; ++ *line = k; ++ + return 0; + } + diff --git a/SOURCES/0078-Revert-sysctl.d-switch-net.ipv4.conf.all.rp_filter-f.patch b/SOURCES/0078-Revert-sysctl.d-switch-net.ipv4.conf.all.rp_filter-f.patch new file mode 100644 index 0000000..8897b48 --- /dev/null +++ b/SOURCES/0078-Revert-sysctl.d-switch-net.ipv4.conf.all.rp_filter-f.patch @@ -0,0 +1,26 @@ +From 47b256d63ac092137fe44e27560a14ee4aa5b7c8 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Fri, 8 Feb 2019 10:54:34 +0100 +Subject: [PATCH] Revert "sysctl.d: switch net.ipv4.conf.all.rp_filter from 1 + to 2" + +This reverts commit 75c9af80cf3529c76988451e63f98010c86f48f1. + +Resolves: #1653824 +--- + sysctl.d/50-default.conf | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sysctl.d/50-default.conf b/sysctl.d/50-default.conf +index b0645f33e7..e263cf0628 100644 +--- a/sysctl.d/50-default.conf ++++ b/sysctl.d/50-default.conf +@@ -22,7 +22,7 @@ kernel.sysrq = 16 + kernel.core_uses_pid = 1 + + # Source route verification +-net.ipv4.conf.all.rp_filter = 2 ++net.ipv4.conf.all.rp_filter = 1 + + # Do not accept source routing + net.ipv4.conf.all.accept_source_route = 0 diff --git a/SOURCES/0079-journal-fix-syslog_parse_identifier.patch b/SOURCES/0079-journal-fix-syslog_parse_identifier.patch new file mode 100644 index 0000000..4d6b283 --- /dev/null +++ b/SOURCES/0079-journal-fix-syslog_parse_identifier.patch @@ -0,0 +1,68 @@ +From d355618518f26bd045df81a52dade79ac3079f3f Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 8 Aug 2018 15:06:36 +0900 +Subject: [PATCH] journal: fix syslog_parse_identifier() + +Fixes #9829. + +(cherry-picked from commit a6aadf4ae0bae185dc4c414d492a4a781c80ffe5) + +Resolves: #1664978 +--- + src/journal/journald-syslog.c | 6 +++--- + src/journal/test-journal-syslog.c | 10 ++++++++-- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c +index 9dea116722..97711ac7a3 100644 +--- a/src/journal/journald-syslog.c ++++ b/src/journal/journald-syslog.c +@@ -194,7 +194,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) + e = l; + l--; + +- if (p[l-1] == ']') { ++ if (l > 0 && p[l-1] == ']') { + size_t k = l-1; + + for (;;) { +@@ -219,8 +219,8 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) + if (t) + *identifier = t; + +- if (strchr(WHITESPACE, p[e])) +- e++; ++ e += strspn(p + e, WHITESPACE); ++ + *buf = p + e; + return e; + } +diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c +index 9ba86f6c8a..05f759817e 100644 +--- a/src/journal/test-journal-syslog.c ++++ b/src/journal/test-journal-syslog.c +@@ -5,8 +5,8 @@ + #include "macro.h" + #include "string-util.h" + +-static void test_syslog_parse_identifier(const char* str, +- const char *ident, const char*pid, int ret) { ++static void test_syslog_parse_identifier(const char *str, ++ const char *ident, const char *pid, int ret) { + const char *buf = str; + _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; + int ret2; +@@ -21,7 +21,13 @@ static void test_syslog_parse_identifier(const char* str, + int main(void) { + test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11); + test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6); ++ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 7); + test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0); ++ test_syslog_parse_identifier(":", "", NULL, 1); ++ test_syslog_parse_identifier(": ", "", NULL, 3); ++ test_syslog_parse_identifier("pidu:", "pidu", NULL, 5); ++ test_syslog_parse_identifier("pidu: ", "pidu", NULL, 6); ++ test_syslog_parse_identifier("pidu : ", NULL, NULL, 0); + + return 0; + } diff --git a/SOURCES/0080-journald-set-a-limit-on-the-number-of-fields-1k.patch b/SOURCES/0080-journald-set-a-limit-on-the-number-of-fields-1k.patch new file mode 100644 index 0000000..dca08e6 --- /dev/null +++ b/SOURCES/0080-journald-set-a-limit-on-the-number-of-fields-1k.patch @@ -0,0 +1,54 @@ +From 9f53d3cded6cf7eccb40c810dfb8fd6e101c7a3b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 22:45:02 +0100 +Subject: [PATCH] journald: set a limit on the number of fields (1k) + +We allocate a iovec entry for each field, so with many short entries, +our memory usage and processing time can be large, even with a relatively +small message size. Let's refuse overly long entries. + +CVE-2018-16865 +https://bugzilla.redhat.com/show_bug.cgi?id=1653861 + +What from I can see, the problem is not from an alloca, despite what the CVE +description says, but from the attack multiplication that comes from creating +many very small iovecs: (void* + size_t) for each three bytes of input message. + +(cherry-picked from commit 052c57f132f04a3cf4148f87561618da1a6908b4) + +Resolves: #1664977 +--- + src/journal/journal-file.h | 3 +++ + src/journal/journald-native.c | 5 +++++ + 2 files changed, 8 insertions(+) + +diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h +index c8114ee2d0..cd8a48a364 100644 +--- a/src/journal/journal-file.h ++++ b/src/journal/journal-file.h +@@ -165,6 +165,9 @@ int journal_file_open_reliably( + * files without adding too many zeros. */ + #define OFSfmt "%06"PRIx64 + ++/* The maximum number of fields in an entry */ ++#define ENTRY_FIELD_COUNT_MAX 1024 ++ + static inline bool VALID_REALTIME(uint64_t u) { + /* This considers timestamps until the year 3112 valid. That should be plenty room... */ + return u > 0 && u < (1ULL << 55); +diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c +index 5ff22a10af..951d092053 100644 +--- a/src/journal/journald-native.c ++++ b/src/journal/journald-native.c +@@ -140,6 +140,11 @@ static int server_process_entry( + } + + /* A property follows */ ++ if (n > ENTRY_FIELD_COUNT_MAX) { ++ log_debug("Received an entry that has more than " STRINGIFY(ENTRY_FIELD_COUNT_MAX) " fields, ignoring entry."); ++ r = 1; ++ goto finish; ++ } + + /* n existing properties, 1 new, +1 for _TRANSPORT */ + if (!GREEDY_REALLOC(iovec, m, diff --git a/SOURCES/0081-journald-when-processing-a-native-message-bail-more-.patch b/SOURCES/0081-journald-when-processing-a-native-message-bail-more-.patch new file mode 100644 index 0000000..1f5f6c1 --- /dev/null +++ b/SOURCES/0081-journald-when-processing-a-native-message-bail-more-.patch @@ -0,0 +1,205 @@ +From ed028441cc2ef0ffb9771d7266d40f18910f0ae1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 22:50:39 +0100 +Subject: [PATCH] journald: when processing a native message, bail more quickly + on overbig messages + +We'd first parse all or most of the message, and only then consider if it +is not too large. Also, when encountering a single field over the limit, +we'd still process the preceding part of the message. Let's be stricter, +and check size limits early, and let's refuse the whole message if it fails +any of the size limits. + +(cherry-picked from commit 964ef920ea6735d39f856b05fd8ef451a09a6a1d) + +Related: #1664977 +--- + src/journal/journald-native.c | 65 ++++++++++++++++++++--------------- + 1 file changed, 37 insertions(+), 28 deletions(-) + +diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c +index 951d092053..110ab3641c 100644 +--- a/src/journal/journald-native.c ++++ b/src/journal/journald-native.c +@@ -109,7 +109,7 @@ static int server_process_entry( + int priority = LOG_INFO; + pid_t object_pid = 0; + const char *p; +- int r = 0; ++ int r = 1; + + p = buffer; + +@@ -121,8 +121,7 @@ static int server_process_entry( + if (!e) { + /* Trailing noise, let's ignore it, and flush what we collected */ + log_debug("Received message with trailing noise, ignoring."); +- r = 1; /* finish processing of the message */ +- break; ++ break; /* finish processing of the message */ + } + + if (e == p) { +@@ -132,8 +131,7 @@ static int server_process_entry( + } + + if (IN_SET(*p, '.', '#')) { +- /* Ignore control commands for now, and +- * comments too. */ ++ /* Ignore control commands for now, and comments too. */ + *remaining -= (e - p) + 1; + p = e + 1; + continue; +@@ -142,7 +140,6 @@ static int server_process_entry( + /* A property follows */ + if (n > ENTRY_FIELD_COUNT_MAX) { + log_debug("Received an entry that has more than " STRINGIFY(ENTRY_FIELD_COUNT_MAX) " fields, ignoring entry."); +- r = 1; + goto finish; + } + +@@ -152,7 +149,7 @@ static int server_process_entry( + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS + + client_context_extra_fields_n_iovec(context))) { + r = log_oom(); +- break; ++ goto finish; + } + + q = memchr(p, '=', e - p); +@@ -161,6 +158,16 @@ static int server_process_entry( + size_t l; + + l = e - p; ++ if (l > DATA_SIZE_MAX) { ++ log_debug("Received text block of %zu bytes is too large, ignoring entry.", l); ++ goto finish; ++ } ++ ++ if (entry_size + l + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ ++ log_debug("Entry is too big (%zu bytes after processing %zu entries), ignoring entry.", ++ entry_size + l, n + 1); ++ goto finish; ++ } + + /* If the field name starts with an underscore, skip the variable, since that indicates + * a trusted field */ +@@ -178,7 +185,7 @@ static int server_process_entry( + p = e + 1; + continue; + } else { +- uint64_t l; ++ uint64_t l, total; + char *k; + + if (*remaining < e - p + 1 + sizeof(uint64_t) + 1) { +@@ -187,10 +194,16 @@ static int server_process_entry( + } + + l = unaligned_read_le64(e + 1); +- + if (l > DATA_SIZE_MAX) { +- log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l); +- break; ++ log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring entry.", l); ++ goto finish; ++ } ++ ++ total = (e - p) + 1 + l; ++ if (entry_size + total + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ ++ log_debug("Entry is too big (%"PRIu64"bytes after processing %zu fields), ignoring.", ++ entry_size + total, n + 1); ++ goto finish; + } + + if ((uint64_t) *remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || +@@ -199,7 +212,7 @@ static int server_process_entry( + break; + } + +- k = malloc((e - p) + 1 + l); ++ k = malloc(total); + if (!k) { + log_oom(); + break; +@@ -228,15 +241,8 @@ static int server_process_entry( + } + } + +- if (n <= 0) { +- r = 1; ++ if (n <= 0) + goto finish; +- } +- +- if (!client_context_test_priority(context, priority)) { +- r = 0; +- goto finish; +- } + + tn = n++; + iovec[tn] = IOVEC_MAKE_STRING("_TRANSPORT=journal"); +@@ -247,6 +253,11 @@ static int server_process_entry( + goto finish; + } + ++ r = 0; /* Success, we read the message. */ ++ ++ if (!client_context_test_priority(context, priority)) ++ goto finish; ++ + if (message) { + if (s->forward_to_syslog) + server_forward_syslog(s, syslog_fixup_facility(priority), identifier, message, ucred, tv); +@@ -318,15 +329,13 @@ void server_process_native_file( + bool sealed; + int r; + +- /* Data is in the passed fd, since it didn't fit in a +- * datagram. */ ++ /* Data is in the passed fd, probably it didn't fit in a datagram. */ + + assert(s); + assert(fd >= 0); + + /* If it's a memfd, check if it is sealed. If so, we can just +- * use map it and use it, and do not need to copy the data +- * out. */ ++ * mmap it and use it, and do not need to copy the data out. */ + sealed = memfd_get_sealed(fd) > 0; + + if (!sealed && (!ucred || ucred->uid != 0)) { +@@ -397,7 +406,7 @@ void server_process_native_file( + ssize_t n; + + if (fstatvfs(fd, &vfs) < 0) { +- log_error_errno(errno, "Failed to stat file system of passed file, ignoring: %m"); ++ log_error_errno(errno, "Failed to stat file system of passed file, not processing it: %m"); + return; + } + +@@ -407,7 +416,7 @@ void server_process_native_file( + * https://github.com/systemd/systemd/issues/1822 + */ + if (vfs.f_flag & ST_MANDLOCK) { +- log_error("Received file descriptor from file system with mandatory locking enabled, refusing."); ++ log_error("Received file descriptor from file system with mandatory locking enabled, not processing it."); + return; + } + +@@ -420,13 +429,13 @@ void server_process_native_file( + * and so is SMB. */ + r = fd_nonblock(fd, true); + if (r < 0) { +- log_error_errno(r, "Failed to make fd non-blocking, ignoring: %m"); ++ log_error_errno(r, "Failed to make fd non-blocking, not processing it: %m"); + return; + } + + /* The file is not sealed, we can't map the file here, since + * clients might then truncate it and trigger a SIGBUS for +- * us. So let's stupidly read it */ ++ * us. So let's stupidly read it. */ + + p = malloc(st.st_size); + if (!p) { diff --git a/SOURCES/0082-journald-lower-the-maximum-entry-size-limit-to-for-n.patch b/SOURCES/0082-journald-lower-the-maximum-entry-size-limit-to-for-n.patch new file mode 100644 index 0000000..0a61460 --- /dev/null +++ b/SOURCES/0082-journald-lower-the-maximum-entry-size-limit-to-for-n.patch @@ -0,0 +1,37 @@ +From a4d1779b5ee28b1c27c509a1baebf881943cad1b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 5 Dec 2018 22:52:53 +0100 +Subject: [PATCH] =?UTF-8?q?journald:=20lower=20the=20maximum=20entry=20siz?= + =?UTF-8?q?e=20limit=20to=20=C2=BD=20for=20non-sealed=20fds?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We immediately read the whole contents into memory, making thigs much more +expensive. Sealed fds should be used instead since they are more efficient +on our side. + +(cherry-picked from commit 6670c9de196c8e2d5e84a8890cbb68f70c4db6e3) + +Related: #1664977 +--- + src/journal/journald-native.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c +index 110ab3641c..da62448ca6 100644 +--- a/src/journal/journald-native.c ++++ b/src/journal/journald-native.c +@@ -380,8 +380,10 @@ void server_process_native_file( + if (st.st_size <= 0) + return; + +- if (st.st_size > ENTRY_SIZE_MAX) { +- log_error("File passed too large. Ignoring."); ++ /* When !sealed, set a lower memory limit. We have to read the file, ++ * effectively doubling memory use. */ ++ if (st.st_size > ENTRY_SIZE_MAX / (sealed ? 1 : 2)) { ++ log_error("File passed too large (%"PRIu64" bytes). Ignoring.", (uint64_t) st.st_size); + return; + } + diff --git a/SOURCES/0083-httpd-use-a-cleanup-function-to-call-MHD_destroy_res.patch b/SOURCES/0083-httpd-use-a-cleanup-function-to-call-MHD_destroy_res.patch new file mode 100644 index 0000000..bf124e4 --- /dev/null +++ b/SOURCES/0083-httpd-use-a-cleanup-function-to-call-MHD_destroy_res.patch @@ -0,0 +1,198 @@ +From c9290315ce840ed1001b897220f3f733811ffc66 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 7 Dec 2018 12:13:10 +0100 +Subject: [PATCH] =?UTF-8?q?=C2=B5httpd:=20use=20a=20cleanup=20function=20t?= + =?UTF-8?q?o=20call=20MHD=5Fdestroy=5Fresponse?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +(cherry-picked from commit d101fb24eb1c58c97f2adce1f69f4b61a788933a) + +Related: #1664977 +--- + src/journal-remote/journal-gatewayd.c | 53 +++++++-------------------- + src/journal-remote/microhttpd-util.c | 11 ++---- + src/journal-remote/microhttpd-util.h | 2 + + 3 files changed, 19 insertions(+), 47 deletions(-) + +diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c +index 9e77e314ff..3a167ab890 100644 +--- a/src/journal-remote/journal-gatewayd.c ++++ b/src/journal-remote/journal-gatewayd.c +@@ -451,7 +451,7 @@ static int request_handler_entries( + struct MHD_Connection *connection, + void *connection_cls) { + +- struct MHD_Response *response; ++ _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL; + RequestMeta *m = connection_cls; + int r; + +@@ -493,11 +493,7 @@ static int request_handler_entries( + return respond_oom(connection); + + MHD_add_response_header(response, "Content-Type", mime_types[m->mode]); +- +- r = MHD_queue_response(connection, MHD_HTTP_OK, response); +- MHD_destroy_response(response); +- +- return r; ++ return MHD_queue_response(connection, MHD_HTTP_OK, response); + } + + static int output_field(FILE *f, OutputMode m, const char *d, size_t l) { +@@ -609,7 +605,7 @@ static int request_handler_fields( + const char *field, + void *connection_cls) { + +- struct MHD_Response *response; ++ _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL; + RequestMeta *m = connection_cls; + int r; + +@@ -632,11 +628,7 @@ static int request_handler_fields( + return respond_oom(connection); + + MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]); +- +- r = MHD_queue_response(connection, MHD_HTTP_OK, response); +- MHD_destroy_response(response); +- +- return r; ++ return MHD_queue_response(connection, MHD_HTTP_OK, response); + } + + static int request_handler_redirect( +@@ -644,8 +636,7 @@ static int request_handler_redirect( + const char *target) { + + char *page; +- struct MHD_Response *response; +- int ret; ++ _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL; + + assert(connection); + assert(target); +@@ -661,11 +652,7 @@ static int request_handler_redirect( + + MHD_add_response_header(response, "Content-Type", "text/html"); + MHD_add_response_header(response, "Location", target); +- +- ret = MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); +- MHD_destroy_response(response); +- +- return ret; ++ return MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); + } + + static int request_handler_file( +@@ -673,8 +660,7 @@ static int request_handler_file( + const char *path, + const char *mime_type) { + +- struct MHD_Response *response; +- int ret; ++ _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL; + _cleanup_close_ int fd = -1; + struct stat st; + +@@ -692,15 +678,10 @@ static int request_handler_file( + response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0); + if (!response) + return respond_oom(connection); +- +- fd = -1; ++ TAKE_FD(fd); + + MHD_add_response_header(response, "Content-Type", mime_type); +- +- ret = MHD_queue_response(connection, MHD_HTTP_OK, response); +- MHD_destroy_response(response); +- +- return ret; ++ return MHD_queue_response(connection, MHD_HTTP_OK, response); + } + + static int get_virtualization(char **v) { +@@ -737,14 +718,13 @@ static int request_handler_machine( + struct MHD_Connection *connection, + void *connection_cls) { + +- struct MHD_Response *response; ++ _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL; + RequestMeta *m = connection_cls; + int r; + _cleanup_free_ char* hostname = NULL, *os_name = NULL; + uint64_t cutoff_from = 0, cutoff_to = 0, usage = 0; +- char *json; + sd_id128_t mid, bid; +- _cleanup_free_ char *v = NULL; ++ _cleanup_free_ char *v = NULL, *json = NULL; + + assert(connection); + assert(m); +@@ -793,21 +773,16 @@ static int request_handler_machine( + usage, + cutoff_from, + cutoff_to); +- + if (r < 0) + return respond_oom(connection); + + response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE); +- if (!response) { +- free(json); ++ if (!response) + return respond_oom(connection); +- } ++ TAKE_PTR(json); + + MHD_add_response_header(response, "Content-Type", "application/json"); +- r = MHD_queue_response(connection, MHD_HTTP_OK, response); +- MHD_destroy_response(response); +- +- return r; ++ return MHD_queue_response(connection, MHD_HTTP_OK, response); + } + + static int request_handler( +diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c +index 34dd9ea555..2ae5172fe9 100644 +--- a/src/journal-remote/microhttpd-util.c ++++ b/src/journal-remote/microhttpd-util.c +@@ -32,21 +32,16 @@ static int mhd_respond_internal(struct MHD_Connection *connection, + const char *buffer, + size_t size, + enum MHD_ResponseMemoryMode mode) { +- struct MHD_Response *response; +- int r; +- + assert(connection); + +- response = MHD_create_response_from_buffer(size, (char*) buffer, mode); ++ _cleanup_(MHD_destroy_responsep) struct MHD_Response *response ++ = MHD_create_response_from_buffer(size, (char*) buffer, mode); + if (!response) + return MHD_NO; + + log_debug("Queueing response %u: %s", code, buffer); + MHD_add_response_header(response, "Content-Type", "text/plain"); +- r = MHD_queue_response(connection, code, response); +- MHD_destroy_response(response); +- +- return r; ++ return MHD_queue_response(connection, code, response); + } + + int mhd_respond(struct MHD_Connection *connection, +diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h +index a50a2a75c7..26909082a1 100644 +--- a/src/journal-remote/microhttpd-util.h ++++ b/src/journal-remote/microhttpd-util.h +@@ -73,3 +73,5 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn + * interesting events without overwhelming detail. + */ + int setup_gnutls_logger(char **categories); ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(struct MHD_Response*, MHD_destroy_response); diff --git a/SOURCES/0084-journal-remote-verify-entry-length-from-header.patch b/SOURCES/0084-journal-remote-verify-entry-length-from-header.patch new file mode 100644 index 0000000..13ec049 --- /dev/null +++ b/SOURCES/0084-journal-remote-verify-entry-length-from-header.patch @@ -0,0 +1,109 @@ +From ad18012c46724aa097f37015a8036a4343206efe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 7 Dec 2018 12:47:14 +0100 +Subject: [PATCH] journal-remote: verify entry length from header +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Calling mhd_respond(), which ulimately calls MHD_queue_response() is +ineffective at point, becuase MHD_queue_response() immediately returns +MHD_NO signifying an error, because the connection is in state +MHD_CONNECTION_CONTINUE_SENT. + +As Christian Grothoff kindly explained: +> You are likely calling MHD_queue_repsonse() too late: once you are +> receiving upload_data, HTTP forces you to process it all. At this time, +> MHD has already sent "100 continue" and cannot take it back (hence you +> get MHD_NO!). +> +> In your request handler, the first time when you are called for a +> connection (and when hence *upload_data_size == 0 and upload_data == +> NULL) you must check the content-length header and react (with +> MHD_queue_response) based on this (to prevent MHD from automatically +> generating 100 continue). + +If we ever encounter this kind of error, print a warning and immediately +abort the connection. (The alternative would be to keep reading the data, +but ignore it, and return an error after we get to the end of data. +That is possible, but of course puts additional load on both the +sender and reciever, and doesn't seem important enough just to return +a good error message.) + +Note that sending of the error does not work (the connection is always aborted +when MHD_queue_response is used with MHD_RESPMEM_MUST_FREE, as in this case) +with libµhttpd 0.59, but works with 0.61: +https://src.fedoraproject.org/rpms/libmicrohttpd/pull-request/1 + +(cherry-picked from commit 7fdb237f5473cb8fc2129e57e8a0039526dcb4fd) + +Related: #1664977 +--- + src/journal-remote/journal-remote-main.c | 34 +++++++++++++++++------- + 1 file changed, 24 insertions(+), 10 deletions(-) + +diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c +index 8fda9d1499..e9b3702e8a 100644 +--- a/src/journal-remote/journal-remote-main.c ++++ b/src/journal-remote/journal-remote-main.c +@@ -210,16 +210,14 @@ static int process_http_upload( + journal_remote_server_global->seal); + if (r == -EAGAIN) + break; +- else if (r < 0) { +- log_warning("Failed to process data for connection %p", connection); ++ if (r < 0) { + if (r == -E2BIG) +- return mhd_respondf(connection, +- r, MHD_HTTP_PAYLOAD_TOO_LARGE, +- "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes."); ++ log_warning_errno(r, "Entry is too above maximum of %u, aborting connection %p.", ++ DATA_SIZE_MAX, connection); + else +- return mhd_respondf(connection, +- r, MHD_HTTP_UNPROCESSABLE_ENTITY, +- "Processing failed: %m."); ++ log_warning_errno(r, "Failed to process data, aborting connection %p: %m", ++ connection); ++ return MHD_NO; + } + } + +@@ -253,6 +251,7 @@ static int request_handler( + const char *header; + int r, code, fd; + _cleanup_free_ char *hostname = NULL; ++ size_t len; + + assert(connection); + assert(connection_cls); +@@ -272,12 +271,27 @@ static int request_handler( + if (!streq(url, "/upload")) + return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found."); + +- header = MHD_lookup_connection_value(connection, +- MHD_HEADER_KIND, "Content-Type"); ++ header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type"); + if (!header || !streq(header, "application/vnd.fdo.journal")) + 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); ++ ++ 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/SOURCES/0085-journal-remote-set-a-limit-on-the-number-of-fields-i.patch b/SOURCES/0085-journal-remote-set-a-limit-on-the-number-of-fields-i.patch new file mode 100644 index 0000000..6ba3347 --- /dev/null +++ b/SOURCES/0085-journal-remote-set-a-limit-on-the-number-of-fields-i.patch @@ -0,0 +1,56 @@ +From fde3fa3e9c0330c7de645ce2140f9dd39640a693 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 7 Dec 2018 10:48:10 +0100 +Subject: [PATCH] journal-remote: set a limit on the number of fields in a + message + +Existing use of E2BIG is replaced with ENOBUFS (entry too long), and E2BIG is +reused for the new error condition (too many fields). + +This matches the change done for systemd-journald, hence forming the second +part of the fix for CVE-2018-16865 +(https://bugzilla.redhat.com/show_bug.cgi?id=1653861). + +(cherry-picked from commit ef4d6abe7c7fab6cbff975b32e76b09feee56074) + +Resolves: #1664977 +--- + src/journal-remote/journal-remote-main.c | 7 +++++-- + src/journal-remote/journal-remote.c | 5 ++++- + 2 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c +index e9b3702e8a..5b0bbba310 100644 +--- a/src/journal-remote/journal-remote-main.c ++++ b/src/journal-remote/journal-remote-main.c +@@ -211,9 +211,12 @@ static int process_http_upload( + if (r == -EAGAIN) + break; + if (r < 0) { +- if (r == -E2BIG) +- log_warning_errno(r, "Entry is too above maximum of %u, aborting connection %p.", ++ if (r == -ENOBUFS) ++ log_warning_errno(r, "Entry is above the maximum of %u, aborting connection %p.", + DATA_SIZE_MAX, connection); ++ else if (r == -E2BIG) ++ log_warning_errno(r, "Entry with more fields than the maximum of %u, aborting connection %p.", ++ ENTRY_FIELD_COUNT_MAX, connection); + else + log_warning_errno(r, "Failed to process data, aborting connection %p: %m", + connection); +diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c +index beb75a1cb4..67e3a70c06 100644 +--- a/src/journal-remote/journal-remote.c ++++ b/src/journal-remote/journal-remote.c +@@ -408,7 +408,10 @@ int journal_remote_handle_raw_source( + log_debug("%zu active sources remaining", s->active); + return 0; + } else if (r == -E2BIG) { +- log_notice_errno(E2BIG, "Entry too big, skipped"); ++ log_notice("Entry with too many fields, skipped"); ++ return 1; ++ } else if (r == -ENOBUFS) { ++ log_notice("Entry too big, skipped"); + return 1; + } else if (r == -EAGAIN) { + return 0; diff --git a/SOURCES/0086-journald-correctly-attribute-log-messages-also-with-.patch b/SOURCES/0086-journald-correctly-attribute-log-messages-also-with-.patch new file mode 100644 index 0000000..f9463d5 --- /dev/null +++ b/SOURCES/0086-journald-correctly-attribute-log-messages-also-with-.patch @@ -0,0 +1,54 @@ +From 7c52627446e32df64ad4cd3ac56ad515d0233cea Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 14 Dec 2018 15:17:27 +0100 +Subject: [PATCH] journald: correctly attribute log messages also with + cgroupsv1 + +With cgroupsv1 a zombie process is migrated to root cgroup in all +hierarchies. This was changed for unified hierarchy and /proc/PID/cgroup +reports cgroup to which process belonged before it exited. + +Be more suspicious about cgroup path reported by the kernel and use +unit_id provided by the log client if the kernel reports that process is +running in the root cgroup. + +Users tend to care the most about 'log->unit_id' mapping so systemctl +status can correctly report last log lines. Also we wouldn't be able to +infer anything useful from "/" path anyway. + +See: https://github.com/torvalds/linux/commit/2e91fa7f6d451e3ea9fec999065d2fd199691f9d + +(cherry picked from commit 672773b63a4ebf95242b27e63071b93073ebc1f5) + +Resolves: #1658115 +--- + src/journal/journald-context.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c +index 51f79fd803..dba3525ed8 100644 +--- a/src/journal/journald-context.c ++++ b/src/journal/journald-context.c +@@ -13,6 +13,8 @@ + #include "io-util.h" + #include "journal-util.h" + #include "journald-context.h" ++#include "parse-util.h" ++#include "path-util.h" + #include "process-util.h" + #include "procfs-util.h" + #include "string-util.h" +@@ -281,9 +283,11 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u + + /* Try to acquire the current cgroup path */ + r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t); +- if (r < 0) { ++ if (r < 0 || empty_or_root(t)) { + +- /* If that didn't work, we use the unit ID passed in as fallback, if we have nothing cached yet */ ++ /* We use the unit ID passed in as fallback if we have nothing cached yet and cg_pid_get_path_shifted() ++ * failed or process is running in a root cgroup. Zombie processes are automatically migrated to root cgroup ++ * on cgroupsv1 and we want to be able to map log messages from them too. */ + if (unit_id && !c->unit) { + c->unit = strdup(unit_id); + if (c->unit) diff --git a/SOURCES/0087-test-replace-echo-with-socat.patch b/SOURCES/0087-test-replace-echo-with-socat.patch new file mode 100644 index 0000000..bbd76ed --- /dev/null +++ b/SOURCES/0087-test-replace-echo-with-socat.patch @@ -0,0 +1,50 @@ +From bf7631e7c0a7d1cac2f071ce998d5c669aa5abd7 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 29 Jan 2019 19:33:15 +0100 +Subject: [PATCH] test: replace echo with socat + +The original version of the test used netcat along with a standard +AF_UNIX socket, which caused issues across different netcat +implementations. The AF_UNIX socket was then replaced by a FIFO with a +simple echo, which, however, suffers from the same issue (some echo +implementations don't check if the write() was successful). + +Let's revert back to the AF_UNIX socket, but replace netcat with socat, +which, hopefully, resolves the main issue. + +Relevant commit: 9b45c2bf02a43e3e1b42de1ab0c3fe29c64dc5f5 + +(cherry picked from commit b35d6d828b3216d022e565820d9971cb0f7746c1) +--- + test/TEST-10-ISSUE-2467/test.sh | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/test/TEST-10-ISSUE-2467/test.sh b/test/TEST-10-ISSUE-2467/test.sh +index e61f5acd3c..0e61236686 100755 +--- a/test/TEST-10-ISSUE-2467/test.sh ++++ b/test/TEST-10-ISSUE-2467/test.sh +@@ -17,7 +17,7 @@ test_setup() { + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment +- dracut_install true rm ++ dracut_install true rm socat + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service <<'EOF' +@@ -29,13 +29,13 @@ After=multi-user.target + Type=oneshot + StandardOutput=tty + StandardError=tty +-ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; echo > /run/test.ctl; >/testok' ++ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; printf x > test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok' + TimeoutStartSec=10s + EOF + + cat >$initdir/etc/systemd/system/test.socket <<'EOF' + [Socket] +-ListenFIFO=/run/test.ctl ++ListenStream=/run/test.ctl + EOF + + cat > $initdir/etc/systemd/system/test.service <<'EOF' diff --git a/SOURCES/0088-test-network-ignore-tunnel-devices-automatically-add.patch b/SOURCES/0088-test-network-ignore-tunnel-devices-automatically-add.patch new file mode 100644 index 0000000..6ec6833 --- /dev/null +++ b/SOURCES/0088-test-network-ignore-tunnel-devices-automatically-add.patch @@ -0,0 +1,25 @@ +From ce123e87018b0345f1027950397f8556bfabb622 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 6 Feb 2019 12:02:15 +0100 +Subject: [PATCH] test-network: ignore tunnel devices automatically added by + kernel + +Fixes #10934. + +(cherry picked from commit e327272d795453f68a4c30ba21eb0e887516cf68) +--- + test/test-execute/exec-privatenetwork-yes.service | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/test-execute/exec-privatenetwork-yes.service b/test/test-execute/exec-privatenetwork-yes.service +index a38d24912f..8f5cbadf04 100644 +--- a/test/test-execute/exec-privatenetwork-yes.service ++++ b/test/test-execute/exec-privatenetwork-yes.service +@@ -2,6 +2,6 @@ + Description=Test for PrivateNetwork + + [Service] +-ExecStart=/bin/sh -x -c '! ip link | grep ": " | grep -Ev ": (lo|sit0@.*):"' ++ExecStart=/bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"' + Type=oneshot + PrivateNetwork=yes diff --git a/SOURCES/0089-rules-add-elevator-kernel-command-line-parameter.patch b/SOURCES/0089-rules-add-elevator-kernel-command-line-parameter.patch new file mode 100644 index 0000000..e289826 --- /dev/null +++ b/SOURCES/0089-rules-add-elevator-kernel-command-line-parameter.patch @@ -0,0 +1,42 @@ +From 1255584bb0a595fb555af7e14230ab1b7aa6adcd Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Tue, 12 Feb 2019 16:58:16 +0100 +Subject: [PATCH] rules: add elevator= kernel command line parameter + +Kernel removed the elevator= option, so let's reintroduce +it for rhel8 via udev rule. + +rhel-only +Resolves: #1670126 +--- + rules/40-elevator.rules | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + create mode 100644 rules/40-elevator.rules + +diff --git a/rules/40-elevator.rules b/rules/40-elevator.rules +new file mode 100644 +index 0000000000..dbe8fc81a4 +--- /dev/null ++++ b/rules/40-elevator.rules +@@ -0,0 +1,20 @@ ++# We aren't adding devices skip the elevator check ++ACTION!="add", GOTO="sched_out" ++ ++SUBSYSTEM!="block", GOTO="sched_out" ++ENV{DEVTYPE}!="disk", GOTO="sched_out" ++ ++# Technically, dm-multipath can be configured to use an I/O scheduler. ++# However, there are races between the 'add' uevent and the linking in ++# of the queue/scheduler sysfs file. For now, just skip dm- devices. ++KERNEL=="dm-*|md*", GOTO="sched_out" ++ ++# Skip bio-based devices, which don't support an I/O scheduler. ++ATTR{queue/scheduler}=="none", GOTO="sched_out" ++ ++# If elevator= is specified on the kernel command line, change the ++# scheduler to the one specified. ++IMPORT{cmdline}="elevator" ++ENV{elevator}!="", ATTR{queue/scheduler}="$env{elevator}" ++ ++LABEL="sched_out" +\ No newline at end of file diff --git a/SOURCES/0090-rule-syntax-check-allow-PROGRAM-as-an-assignment.patch b/SOURCES/0090-rule-syntax-check-allow-PROGRAM-as-an-assignment.patch new file mode 100644 index 0000000..e7de777 --- /dev/null +++ b/SOURCES/0090-rule-syntax-check-allow-PROGRAM-as-an-assignment.patch @@ -0,0 +1,33 @@ +From 1dd326b6dd0a23d1a9ee1c567962c6d5d4ef03ca Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 30 Jan 2019 12:22:41 +0100 +Subject: [PATCH] rule-syntax-check: allow PROGRAM as an assignment + +(cherry picked from commit ed2dc503da57b0110819563e0d1c85d023435e07) +--- + test/rule-syntax-check.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/test/rule-syntax-check.py b/test/rule-syntax-check.py +index 706d93632e..c7c0a1a656 100755 +--- a/test/rule-syntax-check.py ++++ b/test/rule-syntax-check.py +@@ -17,6 +17,8 @@ if not rules_files: + + quoted_string_re = r'"(?:[^\\"]|\\.)*"' + no_args_tests = re.compile(r'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|PROGRAM|RESULT|TEST)\s*(?:=|!)=\s*' + quoted_string_re + '$') ++# PROGRAM can also be specified as an assignment. ++program_assign = re.compile(r'PROGRAM\s*=\s*' + quoted_string_re + '$') + args_tests = re.compile(r'(ATTRS?|ENV|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*' + quoted_string_re + '$') + no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|RUN|LABEL|GOTO|WAIT_FOR|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*' + quoted_string_re + '$') + args_assign = re.compile(r'(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*' + quoted_string_re + '$') +@@ -51,7 +53,8 @@ for path in rules_files: + for clause_match in comma_separated_group_re.finditer(line): + clause = clause_match.group().strip() + if not (no_args_tests.match(clause) or args_tests.match(clause) or +- no_args_assign.match(clause) or args_assign.match(clause)): ++ no_args_assign.match(clause) or args_assign.match(clause) or ++ program_assign.match(clause)): + + print('Invalid line {}:{}: {}'.format(path, lineno, line)) + print(' clause:', clause) diff --git a/SOURCES/0091-rules-implement-new-memory-hotplug-policy.patch b/SOURCES/0091-rules-implement-new-memory-hotplug-policy.patch new file mode 100644 index 0000000..6df4d74 --- /dev/null +++ b/SOURCES/0091-rules-implement-new-memory-hotplug-policy.patch @@ -0,0 +1,50 @@ +From c95026c6979bb6f8dc52a6392318bc16615ace77 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 30 Jan 2019 10:36:53 +0100 +Subject: [PATCH] rules: implement new memory hotplug policy + +Our new policy is based on following motivations (assumptions), + * we want to allow the system to use hotplugged memory + * we want memory ballon inflation to work as expected in VMs (going for small + to big in terms of memory footprint) + * we want to allow memory hotplug and memory hot-unplug on high-end + enterprise server (we assume that node0 will have sufficient memory + resources and marking all memory as movable shouldn't be a problem) + +Policy: + * nevert online memory on s390 (on both physical and z/VM) + * mark memory as "online_movable" on physical machines + * mark memory as "online" in VMs + +If you have the feeling that all this is very wrong and we shouldn't +encode complex policies in udev rules you are absolutely right. However, +for now, we don't have any better place where to put it. In ideal world +we would have a user-space daemon that would be able to configure the +system wrt. to currently present HW and user-defined policy. + +Resolves: #1670728 +--- + rules/40-redhat.rules | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 8ac96933c3..17b33682bd 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -4,7 +4,15 @@ + SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online}="1" + + # Memory hotadd request +-SUBSYSTEM=="memory", ACTION=="add", PROGRAM=="/bin/uname -p", RESULT!="s390*", ATTR{state}=="offline", ATTR{state}="online" ++SUBSYSTEM!="memory", GOTO="memory_hotplug_end" ++ACTION!="add", GOTO="memory_hotplug_end" ++PROGRAM="/bin/uname -p", RESULT=="s390*", GOTO="memory_hotplug_end" ++ ++ENV{.state}="online" ++PROGRAM="/bin/systemd-detect-virt", RESULT=="none", ENV{.state}="online_movable" ++ATTR{state}=="offline", ATTR{state}="$env{.state}" ++ ++LABEL="memory_hotplug_end" + + # reload sysctl.conf / sysctl.conf.d settings when the bridge module is loaded + ACTION=="add", SUBSYSTEM=="module", KERNEL=="bridge", RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/proc/sys/net/bridge" diff --git a/SOURCES/0092-LGTM-make-LGTM.com-use-meson-from-pip.patch b/SOURCES/0092-LGTM-make-LGTM.com-use-meson-from-pip.patch new file mode 100644 index 0000000..5f0f220 --- /dev/null +++ b/SOURCES/0092-LGTM-make-LGTM.com-use-meson-from-pip.patch @@ -0,0 +1,27 @@ +From 9123fecee040fc961905f5c2e56152443907015a Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 24 Jul 2018 01:37:29 +0900 +Subject: [PATCH] LGTM: make LGTM.com use meson from pip + +(cherry picked from commit 55d651d8f69919b98cdc062e312e4454c34428ef) +--- + .lgtm.yml | 10 ++++++++++ + 1 file changed, 10 insertions(+) + create mode 100644 .lgtm.yml + +diff --git a/.lgtm.yml b/.lgtm.yml +new file mode 100644 +index 0000000000..37f9c4335c +--- /dev/null ++++ b/.lgtm.yml +@@ -0,0 +1,10 @@ ++extraction: ++ cpp: ++ prepare: ++ packages: ++ - python3-pip ++ - python3-setuptools ++ - python3-wheel ++ after_prepare: ++ - pip3 install meson ++ - export PATH="$HOME/.local/bin/:$PATH" diff --git a/SOURCES/0093-lgtm-use-python3.patch b/SOURCES/0093-lgtm-use-python3.patch new file mode 100644 index 0000000..f6913c7 --- /dev/null +++ b/SOURCES/0093-lgtm-use-python3.patch @@ -0,0 +1,21 @@ +From dbb6c3db4a9e38dd13525b073b2de1b5eac0ef7d Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Sat, 8 Dec 2018 20:51:56 +0900 +Subject: [PATCH] lgtm: use python3 + +(cherry picked from commit fc1c09e1dfd2073fb49c4e7bb87102c88a551876) +--- + .lgtm.yml | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/.lgtm.yml b/.lgtm.yml +index 37f9c4335c..5948d8c2bc 100644 +--- a/.lgtm.yml ++++ b/.lgtm.yml +@@ -8,3 +8,6 @@ extraction: + after_prepare: + - pip3 install meson + - export PATH="$HOME/.local/bin/:$PATH" ++ python: ++ python_setup: ++ version: 3 diff --git a/SOURCES/0094-tools-use-print-function-in-Python-3-code.patch b/SOURCES/0094-tools-use-print-function-in-Python-3-code.patch new file mode 100644 index 0000000..cc654f7 --- /dev/null +++ b/SOURCES/0094-tools-use-print-function-in-Python-3-code.patch @@ -0,0 +1,73 @@ +From 5adeea95a7310d97b98821322b51794a46199417 Mon Sep 17 00:00:00 2001 +From: Lucas Werkmeister +Date: Sat, 25 Aug 2018 18:41:42 +0200 +Subject: [PATCH] tools: use print function in Python 3 code + +This GDB script was converted to use Python 3 along with all other +Python scripts in commit b95f5528cc, but still used the Python 2 print +statement syntax instead of the Python 3 print function. Fix that. + +We also add the Python 2 compatibility statement, just in case some GDB +still uses Python 2 instead of Python 3. +--- + tools/gdb-sd_dump_hashmaps.py | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/tools/gdb-sd_dump_hashmaps.py b/tools/gdb-sd_dump_hashmaps.py +index e6ddd14ea7..ea15160107 100644 +--- a/tools/gdb-sd_dump_hashmaps.py ++++ b/tools/gdb-sd_dump_hashmaps.py +@@ -2,6 +2,8 @@ + # -*- Mode: python; coding: utf-8; indent-tabs-mode: nil -*- */ + # SPDX-License-Identifier: LGPL-2.1+ + ++from __future__ import print_function ++ + import gdb + + class sd_dump_hashmaps(gdb.Command): +@@ -19,7 +21,7 @@ class sd_dump_hashmaps(gdb.Command): + ulong_t = gdb.lookup_type("unsigned long") + debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") + +- print "type, hash, indirect, entries, max_entries, buckets, creator" ++ print("type, hash, indirect, entries, max_entries, buckets, creator") + while d: + h = gdb.parse_and_eval("(HashmapBase*)((char*)%d - %d)" % (int(d.cast(ulong_t)), debug_offset)) + +@@ -34,7 +36,7 @@ class sd_dump_hashmaps(gdb.Command): + + t = ["plain", "ordered", "set"][int(h["type"])] + +- print "{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"]) ++ print("{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"])) + + if arg != "" and n_entries > 0: + dib_raw_addr = storage_ptr + (all_entry_sizes[h["type"]] * n_buckets) +@@ -46,10 +48,10 @@ class sd_dump_hashmaps(gdb.Command): + + for dib in sorted(iter(histogram)): + if dib != 255: +- print "{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries) ++ print("{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries)) + else: +- print "{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets) +- print "mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries) ++ print("{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets)) ++ print("mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries)) + + blocks = [] + current_len = 1 +@@ -70,9 +72,9 @@ class sd_dump_hashmaps(gdb.Command): + if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1: + blocks[0][1] += blocks[-1][1] + blocks = blocks[0:-1] +- print "max block: {}".format(max(blocks, key=lambda a: a[1])) +- print "sum block lens: {}".format(sum(b[1] for b in blocks)) +- print "mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks))) ++ print("max block: {}".format(max(blocks, key=lambda a: a[1]))) ++ print("sum block lens: {}".format(sum(b[1] for b in blocks))) ++ print("mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks)))) + + d = d["debug_list_next"] + diff --git a/SOURCES/0095-lgtm-add-a-custom-query-for-catching-the-use-of-fget.patch b/SOURCES/0095-lgtm-add-a-custom-query-for-catching-the-use-of-fget.patch new file mode 100644 index 0000000..5cd8a9f --- /dev/null +++ b/SOURCES/0095-lgtm-add-a-custom-query-for-catching-the-use-of-fget.patch @@ -0,0 +1,44 @@ +From c9fdcd0693ac63bc4b7326e248854617d9573bd6 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 26 Oct 2018 09:19:09 +0000 +Subject: [PATCH] lgtm: add a custom query for catching the use of fgets + +As everybody knows, nodoby really reads CODING_STYLE (especially +the last paragraph :-)) so let's utilize LGTM to help us catch the +use of fgets. + +(cherry picked from commit f86c1da28340f2a2afd34d72c9f416a2a94219a8) +--- + .lgtm/cpp-queries/fgets.ql | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + create mode 100644 .lgtm/cpp-queries/fgets.ql + +diff --git a/.lgtm/cpp-queries/fgets.ql b/.lgtm/cpp-queries/fgets.ql +new file mode 100644 +index 0000000000..82de8c4482 +--- /dev/null ++++ b/.lgtm/cpp-queries/fgets.ql +@@ -0,0 +1,23 @@ ++/** ++ * @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/SOURCES/0096-lgtm-drop-redundant-newlines.patch b/SOURCES/0096-lgtm-drop-redundant-newlines.patch new file mode 100644 index 0000000..933e01f --- /dev/null +++ b/SOURCES/0096-lgtm-drop-redundant-newlines.patch @@ -0,0 +1,28 @@ +From d26406cc3f2c186691ce0a09052d3c39d15cc722 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 26 Oct 2018 10:25:36 +0000 +Subject: [PATCH] lgtm: drop redundant newlines + +(cherry picked from commit 845702c63863add5606a7a7f00a959ffdcf89635) +--- + .lgtm/cpp-queries/fgets.ql | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/.lgtm/cpp-queries/fgets.ql b/.lgtm/cpp-queries/fgets.ql +index 82de8c4482..a4181e4f3d 100644 +--- a/.lgtm/cpp-queries/fgets.ql ++++ b/.lgtm/cpp-queries/fgets.ql +@@ -10,13 +10,11 @@ + */ + 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) diff --git a/SOURCES/0097-rules-add-the-rule-that-adds-elevator-kernel-command.patch b/SOURCES/0097-rules-add-the-rule-that-adds-elevator-kernel-command.patch new file mode 100644 index 0000000..c9a43e0 --- /dev/null +++ b/SOURCES/0097-rules-add-the-rule-that-adds-elevator-kernel-command.patch @@ -0,0 +1,24 @@ +From 16d1f6e5122038fa24392e166a0a88c6cab41dd0 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Tue, 26 Feb 2019 15:22:38 +0100 +Subject: [PATCH] rules: add the rule that adds elevator= kernel command line + parameter + +rhel-only +Resolves: #1670126 +--- + rules/meson.build | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/rules/meson.build b/rules/meson.build +index e04a18aca6..b6aae596b6 100644 +--- a/rules/meson.build ++++ b/rules/meson.build +@@ -2,6 +2,7 @@ + + rules = files(''' + 40-redhat.rules ++ 40-elevator.rules + 60-block.rules + 60-cdrom_id.rules + 60-drm.rules diff --git a/SOURCES/0098-test-add-TEST-24-UNIT-TESTS-running-all-basic-tests-.patch b/SOURCES/0098-test-add-TEST-24-UNIT-TESTS-running-all-basic-tests-.patch new file mode 100644 index 0000000..40c595d --- /dev/null +++ b/SOURCES/0098-test-add-TEST-24-UNIT-TESTS-running-all-basic-tests-.patch @@ -0,0 +1,176 @@ +From e4ff044489f43d2c7e1ecbdfb88692d87d63bd2a Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 19 Sep 2018 14:30:29 +0900 +Subject: [PATCH] test: add TEST-24-UNIT-TESTS running all basic tests under + containers + +(cherry picked from commit 3f6f58e03a7d22154aabe036439e7f2fb4849570) +--- + test/TEST-24-UNIT-TESTS/Makefile | 1 + + test/TEST-24-UNIT-TESTS/test.sh | 106 +++++++++++++++++++++++++++ + test/TEST-24-UNIT-TESTS/testsuite.sh | 34 +++++++++ + 3 files changed, 141 insertions(+) + create mode 120000 test/TEST-24-UNIT-TESTS/Makefile + create mode 100755 test/TEST-24-UNIT-TESTS/test.sh + create mode 100755 test/TEST-24-UNIT-TESTS/testsuite.sh + +diff --git a/test/TEST-24-UNIT-TESTS/Makefile b/test/TEST-24-UNIT-TESTS/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-24-UNIT-TESTS/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-24-UNIT-TESTS/test.sh b/test/TEST-24-UNIT-TESTS/test.sh +new file mode 100755 +index 0000000000..014ee52277 +--- /dev/null ++++ b/test/TEST-24-UNIT-TESTS/test.sh +@@ -0,0 +1,106 @@ ++#!/bin/bash ++# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- ++# ex: ts=8 sw=4 sts=4 et filetype=sh ++set -e ++TEST_DESCRIPTION="Run unit tests under containers" ++RUN_IN_UNPRIVILEGED_CONTAINER=yes ++ ++. $TEST_BASE_DIR/test-functions ++ ++check_result_nspawn() { ++ local _ret=1 ++ [[ -e $TESTDIR/$1/testok ]] && _ret=0 ++ if [[ -s $TESTDIR/$1/failed ]]; then ++ _ret=$(($_ret+1)) ++ echo "=== Failed test log ===" ++ cat $TESTDIR/$1/failed ++ else ++ if [[ -s $TESTDIR/$1/skipped ]]; then ++ echo "=== Skipped test log ==" ++ cat $TESTDIR/$1/skipped ++ fi ++ if [[ -s $TESTDIR/$1/testok ]]; then ++ echo "=== Passed tests ===" ++ cat $TESTDIR/$1/testok ++ fi ++ fi ++ cp -a $TESTDIR/$1/var/log/journal $TESTDIR ++ [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1)) ++ return $_ret ++} ++ ++check_result_qemu() { ++ local _ret=1 ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ [[ -e $TESTDIR/root/testok ]] && _ret=0 ++ if [[ -s $TESTDIR/root/failed ]]; then ++ _ret=$(($_ret+1)) ++ echo "=== Failed test log ===" ++ cat $TESTDIR/root/failed ++ else ++ if [[ -s $TESTDIR/root/skipped ]]; then ++ echo "=== Skipped test log ==" ++ cat $TESTDIR/root/skipped ++ fi ++ if [[ -s $TESTDIR/root/testok ]]; then ++ echo "=== Passed tests ===" ++ cat $TESTDIR/root/testok ++ fi ++ fi ++ cp -a $TESTDIR/root/var/log/journal $TESTDIR ++ umount $TESTDIR/root ++ [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1)) ++ return $_ret ++} ++ ++test_setup() { ++ if type -P meson && [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then ++ dfatal "Needs to be built with -Dinstall-tests=true" ++ exit 1 ++ fi ++ ++ create_empty_image ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ ++ # Create what will eventually be our root filesystem onto an overlay ++ ( ++ LOG_LEVEL=5 ++ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) ++ ++ for i in getfacl dirname basename capsh cut rev stat mktemp rmdir ionice unshare uname tr awk getent diff xzcat lz4cat; do ++ inst_binary $i ++ done ++ ++ inst /etc/hosts ++ ++ setup_basic_environment ++ install_keymaps yes ++ install_zoneinfo ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service < /$NAME.log 2>&1 ++ ret=$? ++ if (( $ret && $ret != 77 )); then ++ echo "$NAME failed with $ret" ++ echo $NAME >> /failed-tests ++ echo "--- $NAME begin ---" >> /failed ++ cat /$NAME.log >> /failed ++ echo "--- $NAME end ---" >> /failed ++ elif (( $ret == 77 )); then ++ echo "$NAME skipped" ++ echo $NAME >> /skipped-tests ++ echo "--- $NAME begin ---" >> /skipped ++ cat /$NAME.log >> /skipped ++ echo "--- $NAME end ---" >> /skipped ++ else ++ echo "$NAME OK" ++ echo $NAME >> /testok ++ fi ++ ++ systemd-cat echo "--- $NAME ---" ++ systemd-cat cat /$NAME.log ++done ++ ++exit 0 diff --git a/SOURCES/0099-tests-create-the-asan-wrapper-automatically-if-syste.patch b/SOURCES/0099-tests-create-the-asan-wrapper-automatically-if-syste.patch new file mode 100644 index 0000000..ee842ff --- /dev/null +++ b/SOURCES/0099-tests-create-the-asan-wrapper-automatically-if-syste.patch @@ -0,0 +1,53 @@ +From 34a38e3632fc504b55847fb9658788ccf5d42dad Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Thu, 5 Jul 2018 04:09:30 +0000 +Subject: [PATCH] tests: create the asan wrapper automatically if systemd has + been built with ASAN + +(cherry picked from commit ec9181d2ce4c0ad8b1c70b16a2b02a2667b1cc05) +--- + test/test-functions | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/test/test-functions b/test/test-functions +index e69420aeca..4417301be9 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -28,6 +28,27 @@ STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))" + STATEFILE="$STATEDIR/.testdir" + TESTLOG="$STATEDIR/test.log" + ++is_built_with_asan() { ++ if ! type -P objdump >/dev/null; then ++ ddebug "Failed to find objdump. Assuming systemd hasn't been built with ASAN." ++ return 1 ++ fi ++ ++ # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182 ++ local _asan_calls=$(objdump -dC $BUILD_DIR/systemd | egrep "callq\s+[0-9a-f]+\s+<__asan" -c) ++ if (( $_asan_calls < 1000 )); then ++ return 1 ++ else ++ return 0 ++ fi ++} ++ ++IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no) ++ ++if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then ++ STRIP_BINARIES=no ++fi ++ + function find_qemu_bin() { + # SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm. + # Either way, only use this version if we aren't running in KVM, because +@@ -217,6 +238,9 @@ setup_basic_environment() { + strip_binaries + install_depmod_files + generate_module_dependencies ++ if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then ++ create_asan_wrapper ++ fi + } + + setup_selinux() { diff --git a/SOURCES/0100-tests-add-a-wrapper-for-when-systemd-is-built-with-A.patch b/SOURCES/0100-tests-add-a-wrapper-for-when-systemd-is-built-with-A.patch new file mode 100644 index 0000000..0d49374 --- /dev/null +++ b/SOURCES/0100-tests-add-a-wrapper-for-when-systemd-is-built-with-A.patch @@ -0,0 +1,63 @@ +From 3d0342f0c47c293f10d417ba429ff50436d30ddb Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Tue, 3 Jul 2018 03:25:53 +0000 +Subject: [PATCH] tests: add a wrapper for when systemd is built with ASAN + +(cherry picked from commit 1786fae3668fa94359ee58a8c11031dc46459255) +--- + test/test-functions | 35 ++++++++++++++++++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/test/test-functions b/test/test-functions +index 4417301be9..a6f88e4545 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -21,7 +21,7 @@ if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then + ROOTLIBDIR=/usr/lib/systemd + fi + +-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln" ++BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs" + DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find" + + STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))" +@@ -317,6 +317,39 @@ EOF + chmod 0755 $_valgrind_wrapper + } + ++create_asan_wrapper() { ++ local _asan_wrapper=$initdir/$ROOTLIBDIR/systemd-under-asan ++ ddebug "Create $_asan_wrapper" ++ cat >$_asan_wrapper <>/etc/systemd/system.conf ++ ++# ASAN and syscall filters aren't compatible with each other. ++find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/' ++ ++export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log ++exec $ROOTLIBDIR/systemd "\$@" ++EOF ++ ++ chmod 0755 $_asan_wrapper ++} ++ + create_strace_wrapper() { + local _strace_wrapper=$initdir/$ROOTLIBDIR/systemd-under-strace + ddebug "Create $_strace_wrapper" diff --git a/SOURCES/0101-tests-redirect-ASAN-reports-on-journald-to-a-file.patch b/SOURCES/0101-tests-redirect-ASAN-reports-on-journald-to-a-file.patch new file mode 100644 index 0000000..0f0925a --- /dev/null +++ b/SOURCES/0101-tests-redirect-ASAN-reports-on-journald-to-a-file.patch @@ -0,0 +1,29 @@ +From d0d284178c1ceb2bd13c9b501008c4458a58fe37 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Tue, 3 Jul 2018 19:29:42 +0000 +Subject: [PATCH] tests: redirect ASAN reports on journald to a file + +Otherwise, they will end up in /dev/null. + +(cherry picked from commit 88ed0f261ba8164a689395ddee8b92d00e073515) +--- + test/test-functions | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/test/test-functions b/test/test-functions +index a6f88e4545..822136913b 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -343,6 +343,12 @@ echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf + # ASAN and syscall filters aren't compatible with each other. + find / -name '*.service' -type f | xargs sed -i 's/^\\(MemoryDeny\\|SystemCall\\)/#\\1/' + ++# The redirection of ASAN reports to a file prevents them from ending up in /dev/null. ++# But, apparently, sometimes it doesn't work: https://github.com/google/sanitizers/issues/886. ++JOURNALD_CONF_DIR=/etc/systemd/system/systemd-journald.service.d ++mkdir -p "\$JOURNALD_CONF_DIR" ++printf "[Service]\nEnvironment=ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd-journald.asan.log\n" >"\$JOURNALD_CONF_DIR/env.conf" ++ + export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log + exec $ROOTLIBDIR/systemd "\$@" + EOF diff --git a/SOURCES/0102-tests-use-the-asan-wrapper-to-boot-a-VM-container-if.patch b/SOURCES/0102-tests-use-the-asan-wrapper-to-boot-a-VM-container-if.patch new file mode 100644 index 0000000..5a2ede9 --- /dev/null +++ b/SOURCES/0102-tests-use-the-asan-wrapper-to-boot-a-VM-container-if.patch @@ -0,0 +1,51 @@ +From 26b213f1dc3d869bc8b22607b2e1b5f27717ddc1 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Thu, 5 Jul 2018 15:14:07 +0000 +Subject: [PATCH] tests: use the asan wrapper to boot a VM/container if systemd + is built with ASAN + +(cherry picked from commit 016fa3b9e8c3550d49f659c49b5ff4d93337aefe) +--- + test/test-functions | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/test/test-functions b/test/test-functions +index 822136913b..76eef8aa4d 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -21,6 +21,8 @@ if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then + ROOTLIBDIR=/usr/lib/systemd + 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" + +@@ -47,6 +49,8 @@ IS_BUILT_WITH_ASAN=$(is_built_with_asan && echo yes || echo no) + + if [[ "$IS_BUILT_WITH_ASAN" = "yes" ]]; then + STRIP_BINARIES=no ++ SKIP_INITRD=yes ++ PATH_TO_INIT=$ROOTLIBDIR/systemd-under-asan + fi + + function find_qemu_bin() { +@@ -142,7 +146,7 @@ KERNEL_APPEND="$PARAMS \ + root=/dev/sda1 \ + raid=noautodetect \ + loglevel=2 \ +-init=$ROOTLIBDIR/systemd \ ++init=$PATH_TO_INIT \ + console=ttyS0 \ + selinux=0 \ + printk.devkmsg=on \ +@@ -186,7 +190,7 @@ $KERNEL_APPEND \ + run_nspawn() { + [[ -d /run/systemd/system ]] || return 1 + +- local _nspawn_cmd="$BUILD_DIR/systemd-nspawn --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $ROOTLIBDIR/systemd $KERNEL_APPEND" ++ local _nspawn_cmd="$BUILD_DIR/systemd-nspawn --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $PATH_TO_INIT $KERNEL_APPEND" + if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then + _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd" + fi diff --git a/SOURCES/0103-tests-allow-passing-additional-arguments-to-nspawn-v.patch b/SOURCES/0103-tests-allow-passing-additional-arguments-to-nspawn-v.patch new file mode 100644 index 0000000..54b09cb --- /dev/null +++ b/SOURCES/0103-tests-allow-passing-additional-arguments-to-nspawn-v.patch @@ -0,0 +1,24 @@ +From 164ead52f895785e0461ef205c5be317b400b0ff Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Thu, 5 Jul 2018 16:30:52 +0000 +Subject: [PATCH] tests: allow passing additional arguments to nspawn via + NSPAWN_ARGUMENTS + +(cherry picked from commit 57916ea352b85153ecbed803d52861ca8b933dd3) +--- + test/test-functions | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/test-functions b/test/test-functions +index 76eef8aa4d..8164858c2a 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -190,7 +190,7 @@ $KERNEL_APPEND \ + run_nspawn() { + [[ -d /run/systemd/system ]] || return 1 + +- local _nspawn_cmd="$BUILD_DIR/systemd-nspawn --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $PATH_TO_INIT $KERNEL_APPEND" ++ local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $PATH_TO_INIT $KERNEL_APPEND" + if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then + _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd" + fi diff --git a/SOURCES/0104-tests-also-run-TEST-01-BASIC-in-an-unprivileged-cont.patch b/SOURCES/0104-tests-also-run-TEST-01-BASIC-in-an-unprivileged-cont.patch new file mode 100644 index 0000000..6a693ca --- /dev/null +++ b/SOURCES/0104-tests-also-run-TEST-01-BASIC-in-an-unprivileged-cont.patch @@ -0,0 +1,86 @@ +From 91bd0b915aa17f44625a0c0ce84ed73b6d33968e Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Thu, 30 Aug 2018 07:01:18 +0300 +Subject: [PATCH] tests: also run TEST-01-BASIC in an unprivileged container + (#9957) + +This should make it much easier to catch regressions like +https://github.com/systemd/systemd/issues/9914 and +https://github.com/systemd/systemd/issues/8535. + +(cherry picked from commit 746fbd9c34af5ed8b6d9aa7a9cbd7cac63a3afce) +--- + test/TEST-01-BASIC/test.sh | 1 + + test/test-functions | 23 +++++++++++++++++------ + 2 files changed, 18 insertions(+), 6 deletions(-) + +diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh +index 8b21ba05d3..1d2f833478 100755 +--- a/test/TEST-01-BASIC/test.sh ++++ b/test/TEST-01-BASIC/test.sh +@@ -3,6 +3,7 @@ + # ex: ts=8 sw=4 sts=4 et filetype=sh + set -e + TEST_DESCRIPTION="Basic systemd setup" ++RUN_IN_UNPRIVILEGED_CONTAINER=yes + + . $TEST_BASE_DIR/test-functions + +diff --git a/test/test-functions b/test/test-functions +index 8164858c2a..670c2625f7 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -190,7 +190,7 @@ $KERNEL_APPEND \ + run_nspawn() { + [[ -d /run/systemd/system ]] || return 1 + +- local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/nspawn-root $PATH_TO_INIT $KERNEL_APPEND" ++ local _nspawn_cmd="$BUILD_DIR/systemd-nspawn $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND" + if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then + _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd" + fi +@@ -450,9 +450,9 @@ EOF + + check_result_nspawn() { + ret=1 +- [[ -e $TESTDIR/nspawn-root/testok ]] && ret=0 +- [[ -f $TESTDIR/nspawn-root/failed ]] && cp -a $TESTDIR/nspawn-root/failed $TESTDIR +- cp -a $TESTDIR/nspawn-root/var/log/journal $TESTDIR ++ [[ -e $TESTDIR/$1/testok ]] && ret=0 ++ [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR ++ cp -a $TESTDIR/$1/var/log/journal $TESTDIR + [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed + ls -l $TESTDIR/journal/*/*.journal + test -s $TESTDIR/failed && ret=$(($ret+1)) +@@ -662,6 +662,9 @@ setup_nspawn_root() { + cp -ar $initdir $TESTDIR/nspawn-root + # we don't mount in the nspawn root + rm -f $TESTDIR/nspawn-root/etc/fstab ++ if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then ++ cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root ++ fi + } + + setup_basic_dirs() { +@@ -1478,11 +1481,19 @@ test_run() { + fi + fi + if [ -z "$TEST_NO_NSPAWN" ]; then +- if run_nspawn; then +- check_result_nspawn || return 1 ++ if run_nspawn "nspawn-root"; then ++ check_result_nspawn "nspawn-root" || return 1 + else + dwarn "can't run systemd-nspawn, skipping" + fi ++ ++ if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then ++ if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then ++ check_result_nspawn "unprivileged-nspawn-root" || return 1 ++ else ++ dwarn "can't run systemd-nspawn, skipping" ++ fi ++ fi + fi + return 0 + } diff --git a/SOURCES/0105-test-don-t-overwrite-TESTDIR-if-already-set.patch b/SOURCES/0105-test-don-t-overwrite-TESTDIR-if-already-set.patch new file mode 100644 index 0000000..8667710 --- /dev/null +++ b/SOURCES/0105-test-don-t-overwrite-TESTDIR-if-already-set.patch @@ -0,0 +1,30 @@ +From 9cf9680910544638129bf083bca902881d9ab5bc Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 12 Mar 2019 22:44:25 +0100 +Subject: [PATCH] test: don't overwrite TESTDIR if already set + +(cherry picked from commit 3f50fff536d715aee5e5195ec60e2af047b73c7f) +--- + test/test-functions | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/test/test-functions b/test/test-functions +index 670c2625f7..af9d16140f 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -711,8 +711,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/SOURCES/0106-bus-socket-Fix-line_begins-to-accept-word-matching-f.patch b/SOURCES/0106-bus-socket-Fix-line_begins-to-accept-word-matching-f.patch new file mode 100644 index 0000000..c3d7fc9 --- /dev/null +++ b/SOURCES/0106-bus-socket-Fix-line_begins-to-accept-word-matching-f.patch @@ -0,0 +1,48 @@ +From 94b18b8123b5d957ed84e4aa8e268b60f5427821 Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Tue, 17 Jul 2018 11:32:40 -0700 +Subject: [PATCH] bus-socket: Fix line_begins() to accept word matching full + string + +The switch to memory_startswith() changed the logic to only look for a space or +NUL byte after the matched word, but matching the full size should also be +acceptable. + +This changed the behavior of parsing of "AUTH\r\n", where m will be set to 4, +since even though the word will match, the check for it being followed by ' ' +or NUL will make line_begins() return false. + +Tested: + +- Using netcat to connect to the private socket directly: + $ echo -ne '\0AUTH\r\n' | sudo nc -U /run/systemd/private + REJECTED EXTERNAL ANONYMOUS + +- Running the Ignition blackbox test: + $ sudo sh -c 'PATH=$PWD/bin/amd64:$PATH ./tests.test' + PASS + +Fixes: d27b725abf64a19a6b2f99332b663f17ad046771 +(cherry picked from commit 3f10c66270b74530339b3f466c43874bb40c210f) + +Resolves: #1692991 +--- + src/libsystemd/sd-bus/bus-socket.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c +index b147a3843a..a5513d1ab5 100644 +--- a/src/libsystemd/sd-bus/bus-socket.c ++++ b/src/libsystemd/sd-bus/bus-socket.c +@@ -248,10 +248,7 @@ static bool line_begins(const char *s, size_t m, const char *word) { + const char *p; + + p = memory_startswith(s, m, word); +- if (!p) +- return false; +- +- return IN_SET(*p, 0, ' '); ++ return p && (p == (s + m) || *p == ' '); + } + + static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) { diff --git a/SOURCES/0107-Refuse-dbus-message-paths-longer-than-BUS_PATH_SIZE_.patch b/SOURCES/0107-Refuse-dbus-message-paths-longer-than-BUS_PATH_SIZE_.patch new file mode 100644 index 0000000..025e2e0 --- /dev/null +++ b/SOURCES/0107-Refuse-dbus-message-paths-longer-than-BUS_PATH_SIZE_.patch @@ -0,0 +1,49 @@ +From ac9c51b72213bcea3dc9cea330d4c5fce9c4470e Mon Sep 17 00:00:00 2001 +From: Riccardo Schirone +Date: Mon, 4 Feb 2019 14:29:09 +0100 +Subject: [PATCH] Refuse dbus message paths longer than BUS_PATH_SIZE_MAX + limit. + +Even though the dbus specification does not enforce any length limit on the +path of a dbus message, having to analyze too long strings in PID1 may be +time-consuming and it may have security impacts. + +In any case, the limit is set so high that real-life applications should not +have a problem with it. + +(cherry-picked from commit 61397a60d98e368a5720b37e83f3169e3eb511c4) + +Related: #1678641 +--- + src/libsystemd/sd-bus/bus-internal.c | 2 +- + src/libsystemd/sd-bus/bus-internal.h | 4 ++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c +index 7bb653338d..35e0b668ee 100644 +--- a/src/libsystemd/sd-bus/bus-internal.c ++++ b/src/libsystemd/sd-bus/bus-internal.c +@@ -45,7 +45,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) { +diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h +index 2087ef8eeb..90e6028983 100644 +--- a/src/libsystemd/sd-bus/bus-internal.h ++++ b/src/libsystemd/sd-bus/bus-internal.h +@@ -333,6 +333,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/SOURCES/0108-Allocate-temporary-strings-to-hold-dbus-paths-on-the.patch b/SOURCES/0108-Allocate-temporary-strings-to-hold-dbus-paths-on-the.patch new file mode 100644 index 0000000..47e911d --- /dev/null +++ b/SOURCES/0108-Allocate-temporary-strings-to-hold-dbus-paths-on-the.patch @@ -0,0 +1,189 @@ +From 5eb96e51ca2ce0e90e90ecb9f78e8305f51fa5dd Mon Sep 17 00:00:00 2001 +From: Riccardo Schirone +Date: Mon, 4 Feb 2019 14:29:28 +0100 +Subject: [PATCH] Allocate temporary strings to hold dbus paths on the heap + +Paths are limited to BUS_PATH_SIZE_MAX but the maximum size is anyway too big +to be allocated on the stack, so let's switch to the heap where there is a +clear way to understand if the allocation fails. + +(cherry-picked from commit f519a19bcd5afe674a9b8fc462cd77d8bad403c1) + +Related: #1678641 +--- + src/libsystemd/sd-bus/bus-objects.c | 68 +++++++++++++++++++++++------ + 1 file changed, 54 insertions(+), 14 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c +index a18ff88b07..53bf0fd620 100644 +--- a/src/libsystemd/sd-bus/bus-objects.c ++++ b/src/libsystemd/sd-bus/bus-objects.c +@@ -1134,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); +@@ -1150,7 +1151,12 @@ static int object_manager_serialize_path_and_fallbacks( + return 0; + + /* Second, add fallback vtables registered for any of the prefixes */ +- prefix = alloca(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) +@@ -1346,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; +@@ -1370,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); +@@ -1499,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 = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + n = hashmap_get(bus->nodes, prefix); + if (n) +@@ -2090,8 +2106,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); +@@ -2112,6 +2129,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; + +@@ -2121,7 +2144,6 @@ _public_ int sd_bus_emit_properties_changed_strv( + if (bus->nodes_modified) + continue; + +- prefix = alloca(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) +@@ -2253,7 +2275,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); +@@ -2298,7 +2321,12 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p + if (bus->nodes_modified) + return 0; + +- prefix = alloca(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) +@@ -2437,7 +2465,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); +@@ -2469,7 +2498,12 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char + if (bus->nodes_modified) + return 0; + +- prefix = alloca(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) +@@ -2619,7 +2653,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); +@@ -2633,7 +2668,12 @@ static int interfaces_added_append_one( + if (bus->nodes_modified) + return 0; + +- prefix = alloca(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/SOURCES/0109-sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch b/SOURCES/0109-sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch new file mode 100644 index 0000000..7311983 --- /dev/null +++ b/SOURCES/0109-sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch @@ -0,0 +1,55 @@ +From f2f784ac5e4b7d0e20eadf97049eaec8c685e5fe Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 13 Feb 2019 16:51:22 +0100 +Subject: [PATCH] sd-bus: if we receive an invalid dbus message, ignore and + proceeed + +dbus-daemon might have a slightly different idea of what a valid msg is +than us (for example regarding valid msg and field sizes). Let's hence +try to proceed if we can and thus drop messages rather than fail the +connection if we fail to validate a message. + +Hopefully the differences in what is considered valid are not visible +for real-life usecases, but are specific to exploit attempts only. + +(cherry-picked from commit 6d586a13717ae057aa1b4127400c3de61cd5b9e7) + +Related: #1678641 +--- + src/libsystemd/sd-bus/bus-socket.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c +index a5513d1ab5..17cfa8e1fd 100644 +--- a/src/libsystemd/sd-bus/bus-socket.c ++++ b/src/libsystemd/sd-bus/bus-socket.c +@@ -1078,7 +1078,7 @@ static int bus_socket_read_message_need(sd_bus *bus, size_t *need) { + } + + static int bus_socket_make_message(sd_bus *bus, size_t size) { +- sd_bus_message *t; ++ sd_bus_message *t = NULL; + void *b; + int r; + +@@ -1103,7 +1103,9 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) { + bus->fds, bus->n_fds, + NULL, + &t); +- if (r < 0) { ++ if (r == -EBADMSG) ++ log_debug_errno(r, "Received invalid message from connection %s, dropping.", strna(bus->description)); ++ else if (r < 0) { + free(b); + return r; + } +@@ -1114,7 +1116,8 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) { + bus->fds = NULL; + bus->n_fds = 0; + +- bus->rqueue[bus->rqueue_size++] = t; ++ if (t) ++ bus->rqueue[bus->rqueue_size++] = t; + + return 1; + } diff --git a/SOURCES/0110-meson-drop-misplaced-Wl-undefined-argument.patch b/SOURCES/0110-meson-drop-misplaced-Wl-undefined-argument.patch new file mode 100644 index 0000000..f630599 --- /dev/null +++ b/SOURCES/0110-meson-drop-misplaced-Wl-undefined-argument.patch @@ -0,0 +1,49 @@ +From 0ed1150ccf8837ca85cbb8d5a42fe81b5efeac32 Mon Sep 17 00:00:00 2001 +From: Jussi Pakkanen +Date: Sat, 6 Apr 2019 21:59:06 +0200 +Subject: [PATCH] meson: drop misplaced -Wl,--undefined argument + +Ld's man page says the following: + + -u symbol + --undefined=symbol + + Force symbol to be entered in the output file as an undefined symbol. Doing + this may, for example, trigger linking of additional modules from standard + libraries. -u may be repeated with different option arguments to enter + additional undefined symbols. This option is equivalent to the "EXTERN" + linker script command. + + If this option is being used to force additional modules to be pulled into + the link, and if it is an error for the symbol to remain undefined, then the + option --require-defined should be used instead. + +This would imply that it always requires an argument, which this does not +pass. Thus it will grab the next argument on the command line as its +argument. Before it took one of the many -lrt args (presumably) and now it +grabs something other random linker argument and things break. + +[zj: this line was added in the first version of the meson configuration back +in 5c23128daba7236a6080383b2a5649033cfef85c. AFAICT, this was a mistake. No +such flag appeared in Makefile.am at the time.] + +https://github.com/mesonbuild/meson/issues/5113 +(cherry picked from commit 700805f6c546f2adb79059614f3747f7b5474325) +--- + meson.build | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/meson.build b/meson.build +index d58926c981..f2d67b7e02 100644 +--- a/meson.build ++++ b/meson.build +@@ -1436,8 +1436,7 @@ foreach tuple : [['myhostname', 'ENABLE_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, diff --git a/SOURCES/0111-Revert-core-one-step-back-again-for-nspawn-we-actual.patch b/SOURCES/0111-Revert-core-one-step-back-again-for-nspawn-we-actual.patch new file mode 100644 index 0000000..290f3c8 --- /dev/null +++ b/SOURCES/0111-Revert-core-one-step-back-again-for-nspawn-we-actual.patch @@ -0,0 +1,40 @@ +From 9d0046ceca10911361137d6496987cb15ffff132 Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Thu, 25 Jun 2015 09:20:59 +0200 +Subject: [PATCH] Revert "core: one step back again, for nspawn we actually + can't wait for cgroups running empty since systemd will get exactly zero + notifications about it" + +This reverts commit 743970d2ea6d08aa7c7bff8220f6b7702f2b1db7. + +RHEL-only +https://bugzilla.redhat.com/show_bug.cgi?id=1141137 +https://github.com/systemd/systemd/pull/350 + +Resolves: #1703485 +--- + src/core/unit.c | 11 +---------- + 1 file changed, 1 insertion(+), 10 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index cc43ddc4f1..d298afb0d4 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -4579,16 +4579,7 @@ int unit_kill_context( + + } else if (r > 0) { + +- /* FIXME: For now, on the legacy hierarchy, we will not wait for the cgroup members to die if +- * we are running in a container or if this is a delegation unit, simply because cgroup +- * notification is unreliable in these cases. It doesn't work at all in containers, and outside +- * of containers it can be confused easily by left-over directories in the cgroup — which +- * however should not exist in non-delegated units. On the unified hierarchy that's different, +- * there we get proper events. Hence rely on them. */ +- +- if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 || +- (detect_container() == 0 && !unit_cgroup_delegate(u))) +- wait_for_exit = true; ++ wait_for_exit = true; + + if (send_sighup) { + set_free(pid_set); diff --git a/SOURCES/0112-tree-wide-shorten-error-logging-a-bit.patch b/SOURCES/0112-tree-wide-shorten-error-logging-a-bit.patch new file mode 100644 index 0000000..6640966 --- /dev/null +++ b/SOURCES/0112-tree-wide-shorten-error-logging-a-bit.patch @@ -0,0 +1,795 @@ +From 9bf05059882a8bc80d33877a315e2bd66fe9e1b5 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 7 Aug 2018 10:14:30 +0900 +Subject: [PATCH] tree-wide: shorten error logging a bit + +Continuation of 4027f96aa08c73f109aa46b89842ca0e25c9c0e9. + +(cherry picked from commit 4ae25393f37b96b2b753562a349d68947ab1ad3d) + +Resolves: #1697893 +--- + src/analyze/analyze.c | 30 ++++------ + src/core/dbus-manager.c | 4 +- + src/core/transaction.c | 18 ++---- + src/hostname/hostnamectl.c | 5 +- + src/libsystemd/sd-bus/test-bus-server.c | 6 +- + src/locale/localectl.c | 14 ++--- + src/locale/localed.c | 5 +- + src/login/loginctl.c | 75 +++++++++--------------- + src/login/logind-action.c | 6 +- + src/login/logind-core.c | 4 +- + src/machine/machinectl.c | 78 +++++++++---------------- + src/nspawn/nspawn-register.c | 16 ++--- + src/resolve/resolvectl.c | 6 +- + src/run/run.c | 18 ++---- + src/sulogin-shell/sulogin-shell.c | 4 +- + src/timedate/timedatectl.c | 16 ++--- + 16 files changed, 112 insertions(+), 193 deletions(-) + +diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c +index de0fe6eba8..dc7d2ab0f6 100644 +--- a/src/analyze/analyze.c ++++ b/src/analyze/analyze.c +@@ -156,10 +156,8 @@ static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *in + &error, + 't', val); + +- if (r < 0) { +- log_error("Failed to parse reply: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, -r)); + + return 0; + } +@@ -181,10 +179,8 @@ static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char + property, + &error, + strv); +- if (r < 0) { +- log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, -r)); + + return 0; + } +@@ -368,10 +364,8 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) { + "ListUnits", + &error, &reply, + NULL); +- if (r < 0) { +- log_error("Failed to list units: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) +@@ -967,10 +961,8 @@ static int list_dependencies(sd_bus *bus, const char *name) { + &error, + &reply, + "s"); +- if (r < 0) { +- log_error("Failed to get ID: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "s", &id); + if (r < 0) +@@ -1229,10 +1221,8 @@ static int dot(int argc, char *argv[], void *userdata) { + &error, + &reply, + ""); +- if (r < 0) { +- log_error("Failed to list units: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) +diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c +index d39c9b28c4..b3c011b0df 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -1298,9 +1298,9 @@ int verify_run_space_and_log(const char *message) { + + r = verify_run_space(message, &error); + if (r < 0) +- log_error_errno(r, "%s", bus_error_message(&error, r)); ++ return log_error_errno(r, "%s", bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { +diff --git a/src/core/transaction.c b/src/core/transaction.c +index 1c7efb207a..045930838b 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -695,10 +695,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error + if (r >= 0) + break; + +- if (r != -EAGAIN) { +- log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r)); +- return r; +- } ++ if (r != -EAGAIN) ++ return log_warning_errno(r, "Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r)); + + /* Let's see if the resulting transaction ordering + * graph is still cyclic... */ +@@ -712,10 +710,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error + if (r >= 0) + break; + +- if (r != -EAGAIN) { +- log_warning("Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r)); +- return r; +- } ++ if (r != -EAGAIN) ++ return log_warning_errno(r, "Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r)); + + /* Seventh step: an entry got dropped, let's garbage + * collect its dependencies. */ +@@ -731,10 +727,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error + + /* Ninth step: check whether we can actually apply this */ + r = transaction_is_destructive(tr, mode, e); +- if (r < 0) { +- log_notice("Requested transaction contradicts existing jobs: %s", bus_error_message(e, r)); +- return r; +- } ++ if (r < 0) ++ return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r)); + + /* Tenth step: apply changes */ + r = transaction_apply(tr, m, mode); +diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c +index 8587f5c59f..fa4292c1fc 100644 +--- a/src/hostname/hostnamectl.c ++++ b/src/hostname/hostnamectl.c +@@ -227,8 +227,9 @@ static int set_simple_string(sd_bus *bus, const char *method, const char *value) + &error, NULL, + "sb", value, arg_ask_password); + if (r < 0) +- log_error("Could not set property: %s", bus_error_message(&error, -r)); +- return r; ++ return log_error_errno(r, "Could not set property: %s", bus_error_message(&error, -r)); ++ ++ return 0; + } + + static int set_hostname(int argc, char **argv, void *userdata) { +diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c +index 31b54e252c..f33acda338 100644 +--- a/src/libsystemd/sd-bus/test-bus-server.c ++++ b/src/libsystemd/sd-bus/test-bus-server.c +@@ -130,10 +130,8 @@ static int client(struct context *c) { + return log_error_errno(r, "Failed to allocate method call: %m"); + + r = sd_bus_call(bus, m, 0, &error, &reply); +- if (r < 0) { +- log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r)); + + return 0; + } +diff --git a/src/locale/localectl.c b/src/locale/localectl.c +index b3ad2820d9..ebc6a8ca8a 100644 +--- a/src/locale/localectl.c ++++ b/src/locale/localectl.c +@@ -184,10 +184,8 @@ static int set_locale(int argc, char **argv, void *userdata) { + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, NULL); +- if (r < 0) { +- log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r)); + + return 0; + } +@@ -229,9 +227,9 @@ static int set_vconsole_keymap(int argc, char **argv, void *userdata) { + NULL, + "ssbb", map, toggle_map, arg_convert, arg_ask_password); + if (r < 0) +- log_error("Failed to set keymap: %s", bus_error_message(&error, -r)); ++ return log_error_errno(r, "Failed to set keymap: %s", bus_error_message(&error, -r)); + +- return r; ++ return 0; + } + + static int list_vconsole_keymaps(int argc, char **argv, void *userdata) { +@@ -273,9 +271,9 @@ static int set_x11_keymap(int argc, char **argv, void *userdata) { + "ssssbb", layout, model, variant, options, + arg_convert, arg_ask_password); + if (r < 0) +- log_error("Failed to set keymap: %s", bus_error_message(&error, -r)); ++ return log_error_errno(r, "Failed to set keymap: %s", bus_error_message(&error, -r)); + +- return r; ++ return 0; + } + + static int list_x11_keymaps(int argc, char **argv, void *userdata) { +diff --git a/src/locale/localed.c b/src/locale/localed.c +index b8f95b69a6..253973fd49 100644 +--- a/src/locale/localed.c ++++ b/src/locale/localed.c +@@ -103,8 +103,9 @@ static int vconsole_reload(sd_bus *bus) { + "ss", "systemd-vconsole-setup.service", "replace"); + + if (r < 0) +- log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); +- return r; ++ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r)); ++ ++ return 0; + } + + static int vconsole_convert_to_x11_and_emit(Context *c, sd_bus_message *m) { +diff --git a/src/login/loginctl.c b/src/login/loginctl.c +index be55fdbfd8..9b3fed928b 100644 +--- a/src/login/loginctl.c ++++ b/src/login/loginctl.c +@@ -856,10 +856,9 @@ static int show_session(int argc, char *argv[], void *userdata) { + session = getenv("XDG_SESSION_ID"); + if (session) { + r = get_session_path(bus, session, &error, &path); +- if (r < 0) { +- log_error("Failed to get session path: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r)); ++ + p = path; + } + +@@ -868,10 +867,8 @@ static int show_session(int argc, char *argv[], void *userdata) { + + for (i = 1; i < argc; i++) { + r = get_session_path(bus, argv[i], &error, &path); +- if (r < 0) { +- log_error("Failed to get session path: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r)); + + if (properties) + r = show_properties(bus, path, &new_line); +@@ -924,10 +921,8 @@ static int show_user(int argc, char *argv[], void *userdata) { + "GetUser", + &error, &reply, + "u", (uint32_t) uid); +- if (r < 0) { +- log_error("Failed to get user: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get user: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) +@@ -979,10 +974,8 @@ static int show_seat(int argc, char *argv[], void *userdata) { + "GetSeat", + &error, &reply, + "s", argv[i]); +- if (r < 0) { +- log_error("Failed to get seat: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get seat: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) +@@ -1036,10 +1029,8 @@ static int activate(int argc, char *argv[], void *userdata) { + "ActivateSession", + &error, NULL, + "s", argv[i]); +- if (r < 0) { +- log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1068,10 +1059,8 @@ static int kill_session(int argc, char *argv[], void *userdata) { + "KillSession", + &error, NULL, + "ssi", argv[i], arg_kill_who, arg_signal); +- if (r < 0) { +- log_error("Could not kill session: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not kill session: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1121,10 +1110,8 @@ static int enable_linger(int argc, char *argv[], void *userdata) { + "SetUserLinger", + &error, NULL, + "ubb", (uint32_t) uid, b, true); +- if (r < 0) { +- log_error("Could not enable linger: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not enable linger: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1155,10 +1142,8 @@ static int terminate_user(int argc, char *argv[], void *userdata) { + "TerminateUser", + &error, NULL, + "u", (uint32_t) uid); +- if (r < 0) { +- log_error("Could not terminate user: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not terminate user: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1192,10 +1177,8 @@ static int kill_user(int argc, char *argv[], void *userdata) { + "KillUser", + &error, NULL, + "ui", (uint32_t) uid, arg_signal); +- if (r < 0) { +- log_error("Could not kill user: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not kill user: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1222,10 +1205,8 @@ static int attach(int argc, char *argv[], void *userdata) { + &error, NULL, + "ssb", argv[1], argv[i], true); + +- if (r < 0) { +- log_error("Could not attach device: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not attach device: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1250,9 +1231,9 @@ static int flush_devices(int argc, char *argv[], void *userdata) { + &error, NULL, + "b", true); + if (r < 0) +- log_error("Could not flush devices: %s", bus_error_message(&error, -r)); ++ return log_error_errno(r, "Could not flush devices: %s", bus_error_message(&error, -r)); + +- return r; ++ return 0; + } + + static int lock_sessions(int argc, char *argv[], void *userdata) { +@@ -1274,9 +1255,9 @@ static int lock_sessions(int argc, char *argv[], void *userdata) { + &error, NULL, + NULL); + if (r < 0) +- log_error("Could not lock sessions: %s", bus_error_message(&error, -r)); ++ return log_error_errno(r, "Could not lock sessions: %s", bus_error_message(&error, -r)); + +- return r; ++ return 0; + } + + static int terminate_seat(int argc, char *argv[], void *userdata) { +@@ -1299,10 +1280,8 @@ static int terminate_seat(int argc, char *argv[], void *userdata) { + "TerminateSeat", + &error, NULL, + "s", argv[i]); +- if (r < 0) { +- log_error("Could not terminate seat: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not terminate seat: %s", bus_error_message(&error, -r)); + } + + return 0; +diff --git a/src/login/logind-action.c b/src/login/logind-action.c +index 08e41af81a..317e9ef384 100644 +--- a/src/login/logind-action.c ++++ b/src/login/logind-action.c +@@ -152,10 +152,8 @@ int manager_handle_action( + log_info("%s", message_table[handle]); + + r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error); +- if (r < 0) { +- log_error("Failed to execute operation: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + + return 1; + } +diff --git a/src/login/logind-core.c b/src/login/logind-core.c +index 511e3acf8f..cff5536ac0 100644 +--- a/src/login/logind-core.c ++++ b/src/login/logind-core.c +@@ -522,9 +522,9 @@ int manager_spawn_autovt(Manager *m, unsigned int vtnr) { + NULL, + "ss", name, "fail"); + if (r < 0) +- log_error("Failed to start %s: %s", name, bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to start %s: %s", name, bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static bool manager_is_docked(Manager *m) { +diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c +index d656681daf..e177841c88 100644 +--- a/src/machine/machinectl.c ++++ b/src/machine/machinectl.c +@@ -773,10 +773,8 @@ static int show_machine(int argc, char *argv[], void *userdata) { + &error, + &reply, + "s", argv[i]); +- if (r < 0) { +- log_error("Could not get path to machine: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) +@@ -1118,10 +1116,8 @@ static int show_image(int argc, char *argv[], void *userdata) { + &error, + &reply, + "s", argv[i]); +- if (r < 0) { +- log_error("Could not get path to image: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) +@@ -1158,10 +1154,8 @@ static int kill_machine(int argc, char *argv[], void *userdata) { + &error, + NULL, + "ssi", argv[i], arg_kill_who, arg_signal); +- if (r < 0) { +- log_error("Could not kill machine: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not kill machine: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1200,10 +1194,8 @@ static int terminate_machine(int argc, char *argv[], void *userdata) { + &error, + NULL, + "s", argv[i]); +- if (r < 0) { +- log_error("Could not terminate machine: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, -r)); + } + + return 0; +@@ -1285,10 +1277,8 @@ static int bind_mount(int argc, char *argv[], void *userdata) { + argv[3], + arg_read_only, + arg_mkdir); +- if (r < 0) { +- log_error("Failed to bind mount: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to bind mount: %s", bus_error_message(&error, -r)); + + return 0; + } +@@ -1459,10 +1449,8 @@ static int login_machine(int argc, char *argv[], void *userdata) { + &error, + &reply, + "s", machine); +- if (r < 0) { +- log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "hs", &master, NULL); + if (r < 0) +@@ -1615,10 +1603,8 @@ static int rename_image(int argc, char *argv[], void *userdata) { + &error, + NULL, + "ss", argv[1], argv[2]); +- if (r < 0) { +- log_error("Could not rename image: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not rename image: %s", bus_error_message(&error, -r)); + + return 0; + } +@@ -1681,10 +1667,8 @@ static int read_only_image(int argc, char *argv[], void *userdata) { + &error, + NULL, + "sb", argv[1], b); +- if (r < 0) { +- log_error("Could not mark image read-only: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, -r)); + + return 0; + } +@@ -1773,10 +1757,8 @@ static int start_machine(int argc, char *argv[], void *userdata) { + &error, + &reply, + "ss", unit, "fail"); +- if (r < 0) { +- log_error("Failed to start unit: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to start unit: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) +@@ -1855,10 +1837,8 @@ static int enable_machine(int argc, char *argv[], void *userdata) { + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); +- if (r < 0) { +- log_error("Failed to enable or disable unit: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to enable or disable unit: %s", bus_error_message(&error, -r)); + + if (streq(argv[0], "enable")) { + r = sd_bus_message_read(reply, "b", NULL); +@@ -1993,10 +1973,8 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { + return log_error_errno(r, "Failed to request match: %m"); + + r = sd_bus_call(bus, m, 0, &error, &reply); +- if (r < 0) { +- log_error("Failed to transfer image: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to transfer image: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "uo", &id, NULL); + if (r < 0) +@@ -2430,10 +2408,8 @@ static int list_transfers(int argc, char *argv[], void *userdata) { + &error, + &reply, + NULL); +- if (r < 0) { +- log_error("Could not get transfers: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_enter_container(reply, 'a', "(usssdo)"); + if (r < 0) +@@ -2528,10 +2504,8 @@ static int cancel_transfer(int argc, char *argv[], void *userdata) { + &error, + NULL, + "u", id); +- if (r < 0) { +- log_error("Could not cancel transfer: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, -r)); + } + + return 0; +diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c +index 93185ecaaa..85f3cf1c01 100644 +--- a/src/nspawn/nspawn-register.c ++++ b/src/nspawn/nspawn-register.c +@@ -195,10 +195,8 @@ int register_machine( + r = sd_bus_call(bus, m, 0, &error, NULL); + } + +- if (r < 0) { +- log_error("Failed to register machine: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to register machine: %s", bus_error_message(&error, r)); + + return 0; + } +@@ -242,10 +240,8 @@ int terminate_machine(sd_bus *bus, pid_t pid) { + &error, + NULL, + NULL); +- if (r < 0) { ++ if (r < 0) + log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); +- return 0; +- } + + return 0; + } +@@ -336,10 +332,8 @@ int allocate_scope( + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); +- if (r < 0) { +- log_error("Failed to allocate scope: %s", bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to allocate scope: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) +diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c +index e96c13fea6..cf1ec323a4 100644 +--- a/src/resolve/resolvectl.c ++++ b/src/resolve/resolvectl.c +@@ -274,10 +274,8 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a + ts = now(CLOCK_MONOTONIC); + + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); +- if (r < 0) { +- log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "%s: resolve call failed: %s", pretty, bus_error_message(&error, r)); + + ts = now(CLOCK_MONOTONIC) - ts; + +diff --git a/src/run/run.c b/src/run/run.c +index 2910fcb272..9ad44e7b57 100644 +--- a/src/run/run.c ++++ b/src/run/run.c +@@ -940,10 +940,8 @@ static int start_transient_service( + &error, + &pty_reply, + "s", arg_host); +- if (r < 0) { +- log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to get machine PTY: %s", bus_error_message(&error, -r)); + + r = sd_bus_message_read(pty_reply, "hs", &master, &s); + if (r < 0) +@@ -1219,10 +1217,8 @@ static int start_transient_scope( + polkit_agent_open_if_enabled(arg_transport, arg_ask_password); + + r = sd_bus_call(bus, m, 0, &error, &reply); +- if (r < 0) { +- log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to start transient scope unit: %s", bus_error_message(&error, -r)); + + if (arg_nice_set) { + if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) +@@ -1437,10 +1433,8 @@ static int start_transient_trigger( + polkit_agent_open_if_enabled(arg_transport, arg_ask_password); + + r = sd_bus_call(bus, m, 0, &error, &reply); +- if (r < 0) { +- log_error("Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r)); +- return r; +- } ++ if (r < 0) ++ return log_error_errno(r, "Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r)); + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) +diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c +index d0e5a89f1f..5db3592d6f 100644 +--- a/src/sulogin-shell/sulogin-shell.c ++++ b/src/sulogin-shell/sulogin-shell.c +@@ -59,9 +59,9 @@ static int start_default_target(sd_bus *bus) { + "ss", "default.target", "isolate"); + + if (r < 0) +- log_error("Failed to start default target: %s", bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to start default target: %s", bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static int fork_wait(const char* const cmdline[]) { +diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c +index befc8cb723..a541b01920 100644 +--- a/src/timedate/timedatectl.c ++++ b/src/timedate/timedatectl.c +@@ -204,9 +204,9 @@ static int set_time(int argc, char **argv, void *userdata) { + NULL, + "xbb", (int64_t) t, relative, interactive); + if (r < 0) +- log_error("Failed to set time: %s", bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static int set_timezone(int argc, char **argv, void *userdata) { +@@ -225,9 +225,9 @@ static int set_timezone(int argc, char **argv, void *userdata) { + NULL, + "sb", argv[1], arg_ask_password); + if (r < 0) +- log_error("Failed to set time zone: %s", bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static int set_local_rtc(int argc, char **argv, void *userdata) { +@@ -250,9 +250,9 @@ static int set_local_rtc(int argc, char **argv, void *userdata) { + NULL, + "bbb", b, arg_adjust_system_clock, arg_ask_password); + if (r < 0) +- log_error("Failed to set local RTC: %s", bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static int set_ntp(int argc, char **argv, void *userdata) { +@@ -275,9 +275,9 @@ static int set_ntp(int argc, char **argv, void *userdata) { + NULL, + "bb", b, arg_ask_password); + if (r < 0) +- log_error("Failed to set ntp: %s", bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r)); + +- return r; ++ return 0; + } + + static int list_timezones(int argc, char **argv, void *userdata) { diff --git a/SOURCES/0113-nspawn-simplify-machine-terminate-bus-call.patch b/SOURCES/0113-nspawn-simplify-machine-terminate-bus-call.patch new file mode 100644 index 0000000..85be590 --- /dev/null +++ b/SOURCES/0113-nspawn-simplify-machine-terminate-bus-call.patch @@ -0,0 +1,98 @@ +From 74640adc3e79064ab34f7ced59e231603c58f07c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Oct 2018 22:54:57 +0200 +Subject: [PATCH] nspawn: simplify machine terminate bus call + +We have the machine name anyway, let's use TerminateMachine() on +machined's Manager object directly with it. That way it's a single +method call only, instead of two, to terminate the machine. + +(cherry picked from commit 11d81e506ed68c6c5cebe319dc57a9a2fc4319c5) + +Resolves: #1697893 +--- + src/nspawn/nspawn-register.c | 34 +++++++--------------------------- + src/nspawn/nspawn-register.h | 2 +- + src/nspawn/nspawn.c | 2 +- + 3 files changed, 9 insertions(+), 29 deletions(-) + +diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c +index 85f3cf1c01..e459cb63ec 100644 +--- a/src/nspawn/nspawn-register.c ++++ b/src/nspawn/nspawn-register.c +@@ -201,10 +201,11 @@ int register_machine( + return 0; + } + +-int terminate_machine(sd_bus *bus, pid_t pid) { ++int terminate_machine( ++ sd_bus *bus, ++ const char *machine_name) { ++ + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +- const char *path; + int r; + + assert(bus); +@@ -214,32 +215,11 @@ int terminate_machine(sd_bus *bus, pid_t pid) { + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", +- "GetMachineByPID", +- &error, +- &reply, +- "u", +- (uint32_t) pid); +- if (r < 0) { +- /* Note that the machine might already have been +- * cleaned up automatically, hence don't consider it a +- * failure if we cannot get the machine object. */ +- log_debug("Failed to get machine: %s", bus_error_message(&error, r)); +- return 0; +- } +- +- r = sd_bus_message_read(reply, "o", &path); +- if (r < 0) +- return bus_log_parse_error(r); +- +- r = sd_bus_call_method( +- bus, +- "org.freedesktop.machine1", +- path, +- "org.freedesktop.machine1.Machine", +- "Terminate", ++ "TerminateMachine", + &error, + NULL, +- NULL); ++ "s", ++ machine_name); + if (r < 0) + log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); + +diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h +index 30807b9687..ddd8b053a3 100644 +--- a/src/nspawn/nspawn-register.h ++++ b/src/nspawn/nspawn-register.h +@@ -8,6 +8,6 @@ + #include "nspawn-mount.h" + + int register_machine(sd_bus *bus, const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit, const char *service); +-int terminate_machine(sd_bus *bus, pid_t pid); ++int terminate_machine(sd_bus *bus, const char *machine_name); + + int allocate_scope(sd_bus *bus, const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties); +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index 8aec893a69..c4943f6eb7 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -4066,7 +4066,7 @@ static int run(int master, + + /* Kill if it is not dead yet anyway */ + if (arg_register && !arg_keep_unit && bus) +- terminate_machine(bus, *pid); ++ terminate_machine(bus, arg_machine); + + /* Normally redundant, but better safe than sorry */ + (void) kill(*pid, SIGKILL); diff --git a/SOURCES/0114-nspawn-merge-two-variable-declaration-lines.patch b/SOURCES/0114-nspawn-merge-two-variable-declaration-lines.patch new file mode 100644 index 0000000..d96da2e --- /dev/null +++ b/SOURCES/0114-nspawn-merge-two-variable-declaration-lines.patch @@ -0,0 +1,27 @@ +From 45085ba5ef810dc527f439fe165c5d393443bda9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Oct 2018 22:56:20 +0200 +Subject: [PATCH] nspawn: merge two variable declaration lines + +(cherry picked from commit df61bc5e4aa19f9b211dbe8414343b44361e442c) + +Resolves: #1697893 +--- + src/nspawn/nspawn-register.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c +index e459cb63ec..0d45cce66e 100644 +--- a/src/nspawn/nspawn-register.c ++++ b/src/nspawn/nspawn-register.c +@@ -236,9 +236,8 @@ int allocate_scope( + int kill_signal, + char **properties) { + ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; +- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_free_ char *scope = NULL; + const char *description, *object; diff --git a/SOURCES/0115-nspawn-rework-how-we-allocate-kill-scopes.patch b/SOURCES/0115-nspawn-rework-how-we-allocate-kill-scopes.patch new file mode 100644 index 0000000..c8cb5c3 --- /dev/null +++ b/SOURCES/0115-nspawn-rework-how-we-allocate-kill-scopes.patch @@ -0,0 +1,125 @@ +From 37c1da056b63323514d71d2832a01ea916f004cc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Oct 2018 22:56:40 +0200 +Subject: [PATCH] nspawn: rework how we allocate/kill scopes + +Fixes: #6347 +(cherry picked from commit 1d78fea2d6230e0aafa2603abc8f1f51966ef134) + +Resolves: #1697893 +--- + src/nspawn/nspawn-register.c | 64 +++++++++++++++++++++++++++++++++++- + src/nspawn/nspawn-register.h | 1 + + src/nspawn/nspawn.c | 8 +++-- + 3 files changed, 70 insertions(+), 3 deletions(-) + +diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c +index 0d45cce66e..a7cdfc1c7d 100644 +--- a/src/nspawn/nspawn-register.c ++++ b/src/nspawn/nspawn-register.c +@@ -274,10 +274,12 @@ int allocate_scope( + + description = strjoina("Container ", machine_name); + +- r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)", ++ r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)(sv)", + "PIDs", "au", 1, pid, + "Description", "s", description, + "Delegate", "b", 1, ++ "CollectMode", "s", "inactive-or-failed", ++ "AddRef", "b", 1, + "Slice", "s", isempty(slice) ? SPECIAL_MACHINE_SLICE : slice); + if (r < 0) + return bus_log_create_error(r); +@@ -324,3 +326,63 @@ int allocate_scope( + + return 0; + } ++ ++int terminate_scope( ++ sd_bus *bus, ++ const char *machine_name) { ++ ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _cleanup_free_ char *scope = NULL; ++ int r; ++ ++ r = unit_name_mangle_with_suffix(machine_name, 0, ".scope", &scope); ++ if (r < 0) ++ return log_error_errno(r, "Failed to mangle scope name: %m"); ++ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "AbandonScope", ++ &error, ++ NULL, ++ "s", ++ scope); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to abandon scope '%s', ignoring: %s", scope, bus_error_message(&error, r)); ++ sd_bus_error_free(&error); ++ } ++ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "KillUnit", ++ &error, ++ NULL, ++ "ssi", ++ scope, ++ "all", ++ (int32_t) SIGKILL); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to SIGKILL scope '%s', ignoring: %s", scope, bus_error_message(&error, r)); ++ sd_bus_error_free(&error); ++ } ++ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "UnrefUnit", ++ &error, ++ NULL, ++ "s", ++ scope); ++ if (r < 0) ++ log_debug_errno(r, "Failed to drop reference to scope '%s', ignoring: %s", scope, bus_error_message(&error, r)); ++ ++ return 0; ++} +diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h +index ddd8b053a3..05f5776f23 100644 +--- a/src/nspawn/nspawn-register.h ++++ b/src/nspawn/nspawn-register.h +@@ -11,3 +11,4 @@ int register_machine(sd_bus *bus, const char *machine_name, pid_t pid, const cha + int terminate_machine(sd_bus *bus, const char *machine_name); + + int allocate_scope(sd_bus *bus, const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties); ++int terminate_scope(sd_bus *bus, const char *machine_name); +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index c4943f6eb7..b40411dcd0 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -4065,8 +4065,12 @@ static int run(int master, + putc('\n', stdout); + + /* Kill if it is not dead yet anyway */ +- if (arg_register && !arg_keep_unit && bus) +- terminate_machine(bus, arg_machine); ++ if (bus) { ++ if (arg_register) ++ terminate_machine(bus, arg_machine); ++ else if (!arg_keep_unit) ++ terminate_scope(bus, arg_machine); ++ } + + /* Normally redundant, but better safe than sorry */ + (void) kill(*pid, SIGKILL); diff --git a/SOURCES/0116-unit-enqueue-cgroup-empty-check-event-if-the-last-re.patch b/SOURCES/0116-unit-enqueue-cgroup-empty-check-event-if-the-last-re.patch new file mode 100644 index 0000000..6e68a8c --- /dev/null +++ b/SOURCES/0116-unit-enqueue-cgroup-empty-check-event-if-the-last-re.patch @@ -0,0 +1,31 @@ +From 7b629b3a853c3b1e4e6a916a080996960343d7f2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Oct 2018 23:04:51 +0200 +Subject: [PATCH] unit: enqueue cgroup empty check event if the last ref on a + unit is dropped + +(cherry picked from commit e5c36295d81971ef75d9c6f98f0890b92a4a353f) + +Resolves: #1697893 +--- + src/core/dbus-unit.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index ae0410414e..c5bca10979 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -1746,7 +1746,13 @@ static int bus_unit_track_handler(sd_bus_track *t, void *userdata) { + + u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */ + ++ /* If the client that tracks us disappeared, then there's reason to believe that the cgroup is empty now too, ++ * let's see */ ++ unit_add_to_cgroup_empty_queue(u); ++ ++ /* Also add the unit to the GC queue, after all if the client left it might be time to GC this unit */ + unit_add_to_gc_queue(u); ++ + return 0; + } + diff --git a/SOURCES/0117-Revert-journal-remove-journal-audit-socket.patch b/SOURCES/0117-Revert-journal-remove-journal-audit-socket.patch new file mode 100644 index 0000000..bf88fc8 --- /dev/null +++ b/SOURCES/0117-Revert-journal-remove-journal-audit-socket.patch @@ -0,0 +1,75 @@ +From 7b87977aaa9017c7307cc0645c019b9abd4654d6 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Thu, 2 May 2019 14:08:39 +0200 +Subject: [PATCH] Revert "journal: remove journal audit socket" + +This reverts commit 8618ef2fb30b4139c9bec4e45fb499cd8192a87f. + +Resolves: #1699287 +--- + units/meson.build | 2 ++ + units/systemd-journald-audit.socket | 22 ++++++++++++++++++++++ + units/systemd-journald.service.in | 4 ++-- + 3 files changed, 26 insertions(+), 2 deletions(-) + create mode 100644 units/systemd-journald-audit.socket + +diff --git a/units/meson.build b/units/meson.build +index e54a84ccbf..e4ac6ced64 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -89,6 +89,8 @@ units = [ + 'sockets.target.wants/'], + ['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'], + ['systemd-journal-remote.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'], ++ ['systemd-journald-audit.socket', '', ++ 'sockets.target.wants/'], + ['systemd-journald-dev-log.socket', '', + 'sockets.target.wants/'], + ['systemd-journald.socket', '', +diff --git a/units/systemd-journald-audit.socket b/units/systemd-journald-audit.socket +new file mode 100644 +index 0000000000..cb8b774963 +--- /dev/null ++++ b/units/systemd-journald-audit.socket +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: LGPL-2.1+ ++# ++# This file is part of systemd. ++# ++# systemd is free software; you can redistribute it and/or modify it ++# under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation; either version 2.1 of the License, or ++# (at your option) any later version. ++ ++[Unit] ++Description=Journal Audit Socket ++Documentation=man:systemd-journald.service(8) man:journald.conf(5) ++DefaultDependencies=no ++Before=sockets.target ++ConditionSecurity=audit ++ConditionCapability=CAP_AUDIT_READ ++ ++[Socket] ++Service=systemd-journald.service ++ReceiveBuffer=128M ++ListenNetlink=audit 1 ++PassCredentials=yes +diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in +index 2d5fd0120d..4eab2fa841 100644 +--- a/units/systemd-journald.service.in ++++ b/units/systemd-journald.service.in +@@ -12,12 +12,12 @@ Description=Journal Service + Documentation=man:systemd-journald.service(8) man:journald.conf(5) + DefaultDependencies=no + Requires=systemd-journald.socket +-After=systemd-journald.socket systemd-journald-dev-log.socket syslog.socket ++After=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket syslog.socket + Before=sysinit.target + + [Service] + Type=notify +-Sockets=systemd-journald.socket systemd-journald-dev-log.socket ++Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket + ExecStart=@rootlibexecdir@/systemd-journald + Restart=always + RestartSec=0 diff --git a/SOURCES/0118-journal-don-t-enable-systemd-journald-audit.socket-b.patch b/SOURCES/0118-journal-don-t-enable-systemd-journald-audit.socket-b.patch new file mode 100644 index 0000000..d2fc72e --- /dev/null +++ b/SOURCES/0118-journal-don-t-enable-systemd-journald-audit.socket-b.patch @@ -0,0 +1,39 @@ +From 7a650ee8d3faf79fd5ef866b69741880a3a42b8d Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Thu, 2 May 2019 14:11:54 +0200 +Subject: [PATCH] journal: don't enable systemd-journald-audit.socket by + default + +Resolves: #1699287 +--- + units/meson.build | 3 +-- + units/systemd-journald.service.in | 2 +- + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/units/meson.build b/units/meson.build +index e4ac6ced64..e118d81888 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -89,8 +89,7 @@ units = [ + 'sockets.target.wants/'], + ['systemd-journal-gatewayd.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'], + ['systemd-journal-remote.socket', 'ENABLE_REMOTE HAVE_MICROHTTPD'], +- ['systemd-journald-audit.socket', '', +- 'sockets.target.wants/'], ++ ['systemd-journald-audit.socket', ''], + ['systemd-journald-dev-log.socket', '', + 'sockets.target.wants/'], + ['systemd-journald.socket', '', +diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in +index 4eab2fa841..e109b25792 100644 +--- a/units/systemd-journald.service.in ++++ b/units/systemd-journald.service.in +@@ -17,7 +17,7 @@ Before=sysinit.target + + [Service] + Type=notify +-Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket ++Sockets=systemd-journald.socket systemd-journald-dev-log.socket + ExecStart=@rootlibexecdir@/systemd-journald + Restart=always + RestartSec=0 diff --git a/SOURCES/0119-logs-show-use-grey-color-for-de-emphasizing-journal-.patch b/SOURCES/0119-logs-show-use-grey-color-for-de-emphasizing-journal-.patch new file mode 100644 index 0000000..8290ff9 --- /dev/null +++ b/SOURCES/0119-logs-show-use-grey-color-for-de-emphasizing-journal-.patch @@ -0,0 +1,49 @@ +From 67b548434f258224239e41672478a0038d5c9d30 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 5 Dec 2018 18:42:32 +0100 +Subject: [PATCH] logs-show: use grey color for de-emphasizing journal log + output + +(cherry picked from commit 67df9b7a06d749fdd84f19f7d75ccf0d743f6d72) + +Resolves: #1695601 +--- + src/basic/terminal-util.h | 2 ++ + src/shared/logs-show.c | 4 ++++ + 2 files changed, 6 insertions(+) + +diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h +index c0bd0e67a6..0055b72343 100644 +--- a/src/basic/terminal-util.h ++++ b/src/basic/terminal-util.h +@@ -18,6 +18,7 @@ + #define ANSI_MAGENTA "\x1B[0;35m" + #define ANSI_CYAN "\x1B[0;36m" + #define ANSI_WHITE "\x1B[0;37m" ++#define ANSI_GREY "\x1B[0;2;37m" + + /* Bold/highlighted */ + #define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m" +@@ -129,6 +130,7 @@ DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW); + DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE); + DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA); + DEFINE_ANSI_FUNC(normal, NORMAL); ++DEFINE_ANSI_FUNC(grey, GREY); + + DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL); + DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT); +diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c +index 33afbe2f7f..c66e39d2fe 100644 +--- a/src/shared/logs-show.c ++++ b/src/shared/logs-show.c +@@ -170,6 +170,10 @@ static bool print_multiline( + color_on = ANSI_HIGHLIGHT; + color_off = ANSI_NORMAL; + highlight_on = ANSI_HIGHLIGHT_RED; ++ } else if (priority >= LOG_DEBUG) { ++ color_on = ANSI_GREY; ++ color_off = ANSI_NORMAL; ++ highlight_on = ANSI_HIGHLIGHT_RED; + } + } + diff --git a/SOURCES/0120-units-add-Install-section-to-tmp.mount.patch b/SOURCES/0120-units-add-Install-section-to-tmp.mount.patch new file mode 100644 index 0000000..bafbf50 --- /dev/null +++ b/SOURCES/0120-units-add-Install-section-to-tmp.mount.patch @@ -0,0 +1,24 @@ +From bb3d205bea1c83cbd0e27b504f5f1faa884fb602 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 22 Jan 2019 10:28:42 +0100 +Subject: [PATCH] units: add [Install] section to tmp.mount + +rhel-only + +Resolves: #1667065 +--- + units/tmp.mount | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/units/tmp.mount b/units/tmp.mount +index 742d86385c..b558047030 100644 +--- a/units/tmp.mount ++++ b/units/tmp.mount +@@ -22,3 +22,7 @@ What=tmpfs + Where=/tmp + Type=tmpfs + Options=mode=1777,strictatime,nosuid,nodev ++ ++# Make 'systemctl enable tmp.mount' work: ++[Install] ++WantedBy=local-fs.target diff --git a/SOURCES/0121-nss-do-not-modify-errno-when-NSS_STATUS_NOTFOUND-or-.patch b/SOURCES/0121-nss-do-not-modify-errno-when-NSS_STATUS_NOTFOUND-or-.patch new file mode 100644 index 0000000..fcfeb37 --- /dev/null +++ b/SOURCES/0121-nss-do-not-modify-errno-when-NSS_STATUS_NOTFOUND-or-.patch @@ -0,0 +1,957 @@ +From 34bb0461192c9feba0c0f05a8baf8fefcd9d835e Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Sun, 15 Jul 2018 23:00:00 +0900 +Subject: [PATCH] nss: do not modify errno when NSS_STATUS_NOTFOUND or + NSS_STATUS_SUCCESS + +This also adds PROTECT_ERRNO for all nss module functions. + +C.f. glibc NSS documents https://www.gnu.org/software/libc/manual/html_node/NSS-Modules-Interface.html +and discussion in https://sourceware.org/bugzilla/show_bug.cgi?id=23410. + +Fixes #9585. + +(cherry picked from commit 06202b9e659e5cc72aeecc5200155b7c012fccbc) + +Resolves: #1691691 +--- + src/nss-myhostname/nss-myhostname.c | 16 +++--- + src/nss-mymachines/nss-mymachines.c | 88 ++++++++++++----------------- + src/nss-resolve/nss-resolve.c | 87 +++++++++++++--------------- + src/nss-systemd/nss-systemd.c | 74 +++++++++--------------- + 4 files changed, 108 insertions(+), 157 deletions(-) + +diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c +index f82ce59f2c..5abc0c91bf 100644 +--- a/src/nss-myhostname/nss-myhostname.c ++++ b/src/nss-myhostname/nss-myhostname.c +@@ -45,6 +45,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( + char *r_name; + unsigned n; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -64,7 +65,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r( + + n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses); + if (n_addresses <= 0) { +- *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +@@ -81,7 +81,6 @@ enum nss_status _nss_myhostname_gethostbyname4_r( + + /* We respond to our local host name, our hostname suffixed with a single dot. */ + if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) { +- *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +@@ -157,8 +156,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r( + if (ttlp) + *ttlp = 0; + +- /* Explicitly reset all error variables */ +- *errnop = 0; ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + +@@ -286,8 +285,8 @@ static enum nss_status fill_in_hostent( + if (canonp) + *canonp = r_name; + +- /* Explicitly reset all error variables */ +- *errnop = 0; ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + +@@ -309,6 +308,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( + uint32_t local_address_ipv4 = 0; + int n_addresses = 0; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -334,7 +334,6 @@ enum nss_status _nss_myhostname_gethostbyname3_r( + + n_addresses = local_gateways(NULL, 0, af, &addresses); + if (n_addresses <= 0) { +- *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +@@ -350,7 +349,6 @@ enum nss_status _nss_myhostname_gethostbyname3_r( + } + + if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) { +- *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +@@ -393,6 +391,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( + bool additional_from_hostname = false; + unsigned n; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(addr); +@@ -455,7 +454,6 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( + } + } + +- *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + +diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c +index 8d6caa0ada..9b81cd9ad1 100644 +--- a/src/nss-mymachines/nss-mymachines.c ++++ b/src/nss-mymachines/nss-mymachines.c +@@ -80,6 +80,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( + char *r_name; + int n_ifindices, r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -126,7 +127,6 @@ enum nss_status _nss_mymachines_gethostbyname4_r( + goto fail; + + if (c <= 0) { +- *errnop = ESRCH; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +@@ -200,8 +200,8 @@ enum nss_status _nss_mymachines_gethostbyname4_r( + if (ttlp) + *ttlp = 0; + +- /* Explicitly reset all error variables */ +- *errnop = 0; ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + +@@ -230,6 +230,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( + size_t l, idx, ms, alen; + int r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -278,7 +279,6 @@ enum nss_status _nss_mymachines_gethostbyname3_r( + goto fail; + + if (c <= 0) { +- *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } +@@ -364,8 +364,8 @@ enum nss_status _nss_mymachines_gethostbyname3_r( + if (canonp) + *canonp = r_name; + +- /* Explicitly reset all error variables */ +- *errnop = 0; ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + +@@ -394,6 +394,7 @@ enum nss_status _nss_mymachines_getpwnam_r( + size_t l; + int r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -401,28 +402,28 @@ enum nss_status _nss_mymachines_getpwnam_r( + + p = startswith(name, "vu-"); + if (!p) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + e = strrchr(p, '-'); + if (!e || e == p) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */ +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + r = parse_uid(e + 1, &uid); + if (r < 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + machine = strndupa(p, e - p); + if (!machine_name_is_valid(machine)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) + /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve + * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus + * running on the host. */ +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + r = sd_bus_open_system(&bus); + if (r < 0) +@@ -439,7 +440,7 @@ enum nss_status _nss_mymachines_getpwnam_r( + machine, (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -450,7 +451,7 @@ enum nss_status _nss_mymachines_getpwnam_r( + + /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */ + if (mapped < HOST_UID_LIMIT || mapped == uid) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + l = strlen(name); + if (buflen < l+1) { +@@ -468,13 +469,8 @@ enum nss_status _nss_mymachines_getpwnam_r( + pwd->pw_dir = (char*) "/"; + pwd->pw_shell = (char*) "/sbin/nologin"; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -493,17 +489,18 @@ enum nss_status _nss_mymachines_getpwuid_r( + uint32_t mapped; + int r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!uid_is_valid(uid)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + /* We consider all uids < 65536 host uids */ + if (uid < HOST_UID_LIMIT) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + r = sd_bus_open_system(&bus); + if (r < 0) +@@ -520,7 +517,7 @@ enum nss_status _nss_mymachines_getpwuid_r( + (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -530,7 +527,7 @@ enum nss_status _nss_mymachines_getpwuid_r( + goto fail; + + if (mapped == uid) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { + *errnop = ERANGE; +@@ -545,13 +542,8 @@ enum nss_status _nss_mymachines_getpwuid_r( + pwd->pw_dir = (char*) "/"; + pwd->pw_shell = (char*) "/sbin/nologin"; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -574,6 +566,7 @@ enum nss_status _nss_mymachines_getgrnam_r( + size_t l; + int r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -581,25 +574,25 @@ enum nss_status _nss_mymachines_getgrnam_r( + + p = startswith(name, "vg-"); + if (!p) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + e = strrchr(p, '-'); + if (!e || e == p) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */ +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + r = parse_gid(e + 1, &gid); + if (r < 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + machine = strndupa(p, e - p); + if (!machine_name_is_valid(machine)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + r = sd_bus_open_system(&bus); + if (r < 0) +@@ -616,7 +609,7 @@ enum nss_status _nss_mymachines_getgrnam_r( + machine, (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -626,7 +619,7 @@ enum nss_status _nss_mymachines_getgrnam_r( + goto fail; + + if (mapped < HOST_GID_LIMIT || mapped == gid) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + l = sizeof(char*) + strlen(name) + 1; + if (buflen < l) { +@@ -642,13 +635,8 @@ enum nss_status _nss_mymachines_getgrnam_r( + gr->gr_passwd = (char*) "*"; /* locked */ + gr->gr_mem = (char**) buffer; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -667,17 +655,18 @@ enum nss_status _nss_mymachines_getgrgid_r( + uint32_t mapped; + int r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!gid_is_valid(gid)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + /* We consider all gids < 65536 host gids */ + if (gid < HOST_GID_LIMIT) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + r = sd_bus_open_system(&bus); + if (r < 0) +@@ -694,7 +683,7 @@ enum nss_status _nss_mymachines_getgrgid_r( + (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -704,7 +693,7 @@ enum nss_status _nss_mymachines_getgrgid_r( + goto fail; + + if (mapped == gid) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (buflen < sizeof(char*) + 1) { + *errnop = ERANGE; +@@ -722,13 +711,8 @@ enum nss_status _nss_mymachines_getgrgid_r( + gr->gr_passwd = (char*) "*"; /* locked */ + gr->gr_mem = (char**) buffer; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c +index eb3d2d977f..b2bb698ded 100644 +--- a/src/nss-resolve/nss-resolve.c ++++ b/src/nss-resolve/nss-resolve.c +@@ -108,6 +108,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( + char *r_name; + int c, r, i = 0; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -140,20 +141,15 @@ enum nss_status _nss_resolve_gethostbyname4_r( + + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); + if (r < 0) { +- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { +- *errnop = ESRCH; +- *h_errnop = HOST_NOT_FOUND; +- return NSS_STATUS_NOTFOUND; +- } ++ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") || ++ !bus_error_shall_fallback(&error)) ++ goto not_found; + + /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, + allowing falling back to other nss modules. Treat all other error conditions as + NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this + case so that the nsswitch.conf configuration can distuingish such executed but + negative replies from complete failure to talk to resolved). */ +- if (!bus_error_shall_fallback(&error)) +- ret = NSS_STATUS_NOTFOUND; +- + goto fail; + } + +@@ -162,11 +158,8 @@ enum nss_status _nss_resolve_gethostbyname4_r( + r = c; + goto fail; + } +- if (c == 0) { +- *errnop = ESRCH; +- *h_errnop = HOST_NOT_FOUND; +- return NSS_STATUS_NOTFOUND; +- } ++ if (c == 0) ++ goto not_found; + + if (isempty(canonical)) + canonical = name; +@@ -247,8 +240,8 @@ enum nss_status _nss_resolve_gethostbyname4_r( + if (ttlp) + *ttlp = 0; + +- /* Explicitly reset all error variables */ +- *errnop = 0; ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + +@@ -258,6 +251,10 @@ fail: + *errnop = -r; + *h_errnop = NO_RECOVERY; + return ret; ++ ++not_found: ++ *h_errnop = HOST_NOT_FOUND; ++ return NSS_STATUS_NOTFOUND; + } + + enum nss_status _nss_resolve_gethostbyname3_r( +@@ -278,6 +275,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( + const char *canonical; + int c, r, i = 0; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -318,14 +316,9 @@ enum nss_status _nss_resolve_gethostbyname3_r( + + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); + if (r < 0) { +- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { +- *errnop = ESRCH; +- *h_errnop = HOST_NOT_FOUND; +- return NSS_STATUS_NOTFOUND; +- } +- +- if (!bus_error_shall_fallback(&error)) +- ret = NSS_STATUS_NOTFOUND; ++ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") || ++ !bus_error_shall_fallback(&error)) ++ goto not_found; + + goto fail; + } +@@ -335,11 +328,8 @@ enum nss_status _nss_resolve_gethostbyname3_r( + r = c; + goto fail; + } +- if (c == 0) { +- *errnop = ESRCH; +- *h_errnop = HOST_NOT_FOUND; +- return NSS_STATUS_NOTFOUND; +- } ++ if (c == 0) ++ goto not_found; + + if (isempty(canonical)) + canonical = name; +@@ -427,23 +417,27 @@ enum nss_status _nss_resolve_gethostbyname3_r( + result->h_length = alen; + result->h_addr_list = (char**) r_addr_list; + +- /* Explicitly reset all error variables */ +- *errnop = 0; +- *h_errnop = NETDB_SUCCESS; +- h_errno = 0; +- + if (ttlp) + *ttlp = 0; + + if (canonp) + *canonp = r_name; + ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ ++ *h_errnop = NETDB_SUCCESS; ++ h_errno = 0; ++ + return NSS_STATUS_SUCCESS; + + fail: + *errnop = -r; + *h_errnop = NO_RECOVERY; + return ret; ++ ++not_found: ++ *h_errnop = HOST_NOT_FOUND; ++ return NSS_STATUS_NOTFOUND; + } + + enum nss_status _nss_resolve_gethostbyaddr2_r( +@@ -464,6 +458,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + const char *n; + int r, ifindex; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(addr); +@@ -516,14 +511,9 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); + if (r < 0) { +- if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { +- *errnop = ESRCH; +- *h_errnop = HOST_NOT_FOUND; +- return NSS_STATUS_NOTFOUND; +- } +- +- if (!bus_error_shall_fallback(&error)) +- ret = NSS_STATUS_NOTFOUND; ++ if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") || ++ !bus_error_shall_fallback(&error)) ++ goto not_found; + + goto fail; + } +@@ -549,11 +539,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + if (r < 0) + return r; + +- if (c <= 0) { +- *errnop = ESRCH; +- *h_errnop = HOST_NOT_FOUND; +- return NSS_STATUS_NOTFOUND; +- } ++ if (c <= 0) ++ goto not_found; + + ms += ALIGN(len) + /* the address */ + 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */ +@@ -612,8 +599,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + if (ttlp) + *ttlp = 0; + +- /* Explicitly reset all error variables */ +- *errnop = 0; ++ /* Explicitly reset both *h_errnop and h_errno to work around ++ * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */ + *h_errnop = NETDB_SUCCESS; + h_errno = 0; + +@@ -623,6 +610,10 @@ fail: + *errnop = -r; + *h_errnop = NO_RECOVERY; + return ret; ++ ++not_found: ++ *h_errnop = HOST_NOT_FOUND; ++ return NSS_STATUS_NOTFOUND; + } + + NSS_GETHOSTBYNAME_FALLBACKS(resolve); +diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c +index f516b84c63..f554828d49 100644 +--- a/src/nss-systemd/nss-systemd.c ++++ b/src/nss-systemd/nss-systemd.c +@@ -145,6 +145,7 @@ enum nss_status _nss_systemd_getpwnam_r( + size_t l; + int bypass, r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); +@@ -153,26 +154,24 @@ enum nss_status _nss_systemd_getpwnam_r( + /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't + * generate EINVAL here, because it isn't really out business to complain about invalid user names. */ + if (!valid_user_group_name(name)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (streq(name, root_passwd.pw_name)) { + *pwd = root_passwd; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + streq(name, nobody_passwd.pw_name)) { + *pwd = nobody_passwd; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + } + + /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { +@@ -184,7 +183,7 @@ enum nss_status _nss_systemd_getpwnam_r( + if (bypass > 0) { + r = direct_lookup_name(name, (uid_t*) &translated); + if (r == -ENOENT) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + } else { +@@ -199,7 +198,7 @@ enum nss_status _nss_systemd_getpwnam_r( + name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -225,13 +224,8 @@ enum nss_status _nss_systemd_getpwnam_r( + pwd->pw_dir = (char*) DYNAMIC_USER_DIR; + pwd->pw_shell = (char*) DYNAMIC_USER_SHELL; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -251,31 +245,30 @@ enum nss_status _nss_systemd_getpwuid_r( + size_t l; + int bypass, r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!uid_is_valid(uid)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (uid == root_passwd.pw_uid) { + *pwd = root_passwd; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + uid == nobody_passwd.pw_uid) { + *pwd = nobody_passwd; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + } + + if (!uid_is_dynamic(uid)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { +@@ -287,7 +280,7 @@ enum nss_status _nss_systemd_getpwuid_r( + if (bypass > 0) { + r = direct_lookup_uid(uid, &direct); + if (r == -ENOENT) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + +@@ -305,7 +298,7 @@ enum nss_status _nss_systemd_getpwuid_r( + (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -331,13 +324,8 @@ enum nss_status _nss_systemd_getpwuid_r( + pwd->pw_dir = (char*) DYNAMIC_USER_DIR; + pwd->pw_shell = (char*) DYNAMIC_USER_SHELL; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -358,31 +346,30 @@ enum nss_status _nss_systemd_getgrnam_r( + size_t l; + int bypass, r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(gr); + + if (!valid_user_group_name(name)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + /* Synthesize records for root and nobody, in case they are missing form /etc/group */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (streq(name, root_group.gr_name)) { + *gr = root_group; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + streq(name, nobody_group.gr_name)) { + *gr = nobody_group; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + } + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { +@@ -394,7 +381,7 @@ enum nss_status _nss_systemd_getgrnam_r( + if (bypass > 0) { + r = direct_lookup_name(name, (uid_t*) &translated); + if (r == -ENOENT) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + } else { +@@ -409,7 +396,7 @@ enum nss_status _nss_systemd_getgrnam_r( + name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -433,13 +420,8 @@ enum nss_status _nss_systemd_getgrnam_r( + gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD; + gr->gr_mem = (char**) buffer; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -459,31 +441,30 @@ enum nss_status _nss_systemd_getgrgid_r( + size_t l; + int bypass, r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!gid_is_valid(gid)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + /* Synthesize records for root and nobody, in case they are missing from /etc/group */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (gid == root_group.gr_gid) { + *gr = root_group; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + gid == nobody_group.gr_gid) { + *gr = nobody_group; +- *errnop = 0; + return NSS_STATUS_SUCCESS; + } + } + + if (!gid_is_dynamic(gid)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { +@@ -495,7 +476,7 @@ enum nss_status _nss_systemd_getgrgid_r( + if (bypass > 0) { + r = direct_lookup_uid(gid, &direct); + if (r == -ENOENT) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + +@@ -513,7 +494,7 @@ enum nss_status _nss_systemd_getgrgid_r( + (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) +- goto not_found; ++ return NSS_STATUS_NOTFOUND; + + goto fail; + } +@@ -537,13 +518,8 @@ enum nss_status _nss_systemd_getgrgid_r( + gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD; + gr->gr_mem = (char**) buffer; + +- *errnop = 0; + return NSS_STATUS_SUCCESS; + +-not_found: +- *errnop = 0; +- return NSS_STATUS_NOTFOUND; +- + fail: + *errnop = -r; + return NSS_STATUS_UNAVAIL; +@@ -598,6 +574,7 @@ static void systemd_endent(GetentData *data) { + } + + static enum nss_status nss_systemd_endent(GetentData *p) { ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert_se(pthread_mutex_lock(&p->mutex) == 0); +@@ -668,6 +645,7 @@ static enum nss_status systemd_setent(GetentData *p) { + uid_t id; + int bypass, r; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(p); +@@ -750,6 +728,7 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz + UserEntry *p; + size_t len; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(result); +@@ -778,7 +757,6 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz + break; + } + if (!p) { +- *errnop = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto finalize; + } +@@ -801,6 +779,7 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size + UserEntry *p; + size_t len; + ++ PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(result); +@@ -827,7 +806,6 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size + break; + } + if (!p) { +- *errnop = ENOENT; + ret = NSS_STATUS_NOTFOUND; + goto finalize; + } diff --git a/SOURCES/0122-util.h-add-new-UNPROTECT_ERRNO-macro.patch b/SOURCES/0122-util.h-add-new-UNPROTECT_ERRNO-macro.patch new file mode 100644 index 0000000..5691001 --- /dev/null +++ b/SOURCES/0122-util.h-add-new-UNPROTECT_ERRNO-macro.patch @@ -0,0 +1,103 @@ +From b05795bd3d8afcdcb765639a636ada8d36a2ee79 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 18 Jan 2019 20:04:13 +0100 +Subject: [PATCH] util.h: add new UNPROTECT_ERRNO macro + +THis is inspired by #11395, but much simpler. + +(cherry picked from commit 840f606d88fef2f5d240b2d759ce7b951354d5bb) + +Resolves: #1691691 +--- + src/basic/util.h | 9 +++++++++ + src/test/test-fs-util.c | 6 +++--- + src/test/test-util.c | 25 +++++++++++++++++++++++++ + 3 files changed, 37 insertions(+), 3 deletions(-) + +diff --git a/src/basic/util.h b/src/basic/util.h +index 9699d228f9..27b5a09782 100644 +--- a/src/basic/util.h ++++ b/src/basic/util.h +@@ -134,11 +134,20 @@ static inline void *mempset(void *s, int c, size_t 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_) __attribute__((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();" +diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c +index fc650b513e..7b7990bb70 100644 +--- a/src/test/test-fs-util.c ++++ b/src/test/test-fs-util.c +@@ -322,11 +322,11 @@ static void test_unlink_noerrno(void) { + + { + PROTECT_ERRNO; +- errno = -42; ++ errno = 42; + assert_se(unlink_noerrno(name) >= 0); +- assert_se(errno == -42); ++ assert_se(errno == 42); + assert_se(unlink_noerrno(name) < 0); +- assert_se(errno == -42); ++ assert_se(errno == 42); + } + } + +diff --git a/src/test/test-util.c b/src/test/test-util.c +index 4d3e5c5b94..df60d89115 100644 +--- a/src/test/test-util.c ++++ b/src/test/test-util.c +@@ -164,6 +164,30 @@ static void test_protect_errno(void) { + assert_se(errno == 12); + } + ++static void test_unprotect_errno_inner_function(void) { ++ PROTECT_ERRNO; ++ ++ errno = 2222; ++} ++ ++static void test_unprotect_errno(void) { ++ log_info("/* %s */", __func__); ++ ++ errno = 4711; ++ ++ PROTECT_ERRNO; ++ ++ errno = 815; ++ ++ UNPROTECT_ERRNO; ++ ++ assert_se(errno == 4711); ++ ++ test_unprotect_errno_inner_function(); ++ ++ assert_se(errno == 4711); ++} ++ + static void test_in_set(void) { + assert_se(IN_SET(1, 1)); + assert_se(IN_SET(1, 1, 2, 3, 4)); +@@ -307,6 +331,7 @@ int main(int argc, char *argv[]) { + test_div_round_up(); + test_u64log2(); + test_protect_errno(); ++ test_unprotect_errno(); + test_in_set(); + test_log2i(); + test_raw_clone(); diff --git a/SOURCES/0123-nss-unportect-errno-before-writing-to-NSS-errnop.patch b/SOURCES/0123-nss-unportect-errno-before-writing-to-NSS-errnop.patch new file mode 100644 index 0000000..8ed4d22 --- /dev/null +++ b/SOURCES/0123-nss-unportect-errno-before-writing-to-NSS-errnop.patch @@ -0,0 +1,367 @@ +From ecc4a34067ed7c03b9ee710aa5e5976fded48c2a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 18 Jan 2019 20:13:55 +0100 +Subject: [PATCH] nss: unportect errno before writing to NSS' *errnop + +Fixes: #11321 +(cherry picked from commit cdccd29f39cd20cb2a8b71e50445eb839f076331) + +Resolves: #1691691 +--- + src/nss-myhostname/nss-myhostname.c | 13 +++++++++++++ + src/nss-mymachines/nss-mymachines.c | 13 +++++++++++++ + src/nss-resolve/nss-resolve.c | 8 ++++++++ + src/nss-systemd/nss-systemd.c | 10 ++++++++++ + 4 files changed, 44 insertions(+) + +diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c +index 5abc0c91bf..e491351dee 100644 +--- a/src/nss-myhostname/nss-myhostname.c ++++ b/src/nss-myhostname/nss-myhostname.c +@@ -74,6 +74,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( + } else { + hn = gethostname_malloc(); + if (!hn) { ++ UNPROTECT_ERRNO; + *errnop = ENOMEM; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_TRYAGAIN; +@@ -96,6 +97,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( + l = strlen(canonical); + ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2); + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -186,6 +188,8 @@ static enum nss_status fill_in_hostent( + assert(errnop); + assert(h_errnop); + ++ PROTECT_ERRNO; ++ + alen = FAMILY_ADDRESS_SIZE(af); + + for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++) +@@ -202,6 +206,7 @@ static enum nss_status fill_in_hostent( + (c > 0 ? c+1 : 2) * sizeof(char*); + + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -321,6 +326,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( + af = AF_INET; + + if (!IN_SET(af, AF_INET, AF_INET6)) { ++ UNPROTECT_ERRNO; + *errnop = EAFNOSUPPORT; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; +@@ -343,6 +349,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( + } else { + hn = gethostname_malloc(); + if (!hn) { ++ UNPROTECT_ERRNO; + *errnop = ENOMEM; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_TRYAGAIN; +@@ -362,6 +369,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r( + local_address_ipv4 = LOCALADDRESS_IPV4; + } + ++ UNPROTECT_ERRNO; ++ + return fill_in_hostent( + canonical, additional, + af, +@@ -401,12 +410,14 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( + assert(h_errnop); + + if (!IN_SET(af, AF_INET, AF_INET6)) { ++ UNPROTECT_ERRNO; + *errnop = EAFNOSUPPORT; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; + } + + if (len != FAMILY_ADDRESS_SIZE(af)) { ++ UNPROTECT_ERRNO; + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; +@@ -461,6 +472,7 @@ found: + if (!canonical || additional_from_hostname) { + hn = gethostname_malloc(); + if (!hn) { ++ UNPROTECT_ERRNO; + *errnop = ENOMEM; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_TRYAGAIN; +@@ -472,6 +484,7 @@ found: + additional = hn; + } + ++ UNPROTECT_ERRNO; + return fill_in_hostent( + canonical, additional, + af, +diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c +index 9b81cd9ad1..af9f0bf41f 100644 +--- a/src/nss-mymachines/nss-mymachines.c ++++ b/src/nss-mymachines/nss-mymachines.c +@@ -134,6 +134,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( + l = strlen(name); + ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -208,6 +209,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; +@@ -289,6 +291,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( + ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); + + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -372,6 +375,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; +@@ -455,6 +459,7 @@ enum nss_status _nss_mymachines_getpwnam_r( + + l = strlen(name); + if (buflen < l+1) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -472,6 +477,7 @@ enum nss_status _nss_mymachines_getpwnam_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -530,6 +536,7 @@ enum nss_status _nss_mymachines_getpwuid_r( + return NSS_STATUS_NOTFOUND; + + if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -545,6 +552,7 @@ enum nss_status _nss_mymachines_getpwuid_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -623,6 +631,7 @@ enum nss_status _nss_mymachines_getgrnam_r( + + l = sizeof(char*) + strlen(name) + 1; + if (buflen < l) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -638,6 +647,7 @@ enum nss_status _nss_mymachines_getgrnam_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -696,12 +706,14 @@ enum nss_status _nss_mymachines_getgrgid_r( + return NSS_STATUS_NOTFOUND; + + if (buflen < sizeof(char*) + 1) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -714,6 +726,7 @@ enum nss_status _nss_mymachines_getgrgid_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c +index b2bb698ded..145dbdd60c 100644 +--- a/src/nss-resolve/nss-resolve.c ++++ b/src/nss-resolve/nss-resolve.c +@@ -167,6 +167,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( + l = strlen(canonical); + ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -248,6 +249,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + *h_errnop = NO_RECOVERY; + return ret; +@@ -340,6 +342,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( + ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); + + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -431,6 +434,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + *h_errnop = NO_RECOVERY; + return ret; +@@ -468,12 +472,14 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + assert(h_errnop); + + if (!IN_SET(af, AF_INET, AF_INET6)) { ++ UNPROTECT_ERRNO; + *errnop = EAFNOSUPPORT; + *h_errnop = NO_DATA; + return NSS_STATUS_UNAVAIL; + } + + if (len != FAMILY_ADDRESS_SIZE(af)) { ++ UNPROTECT_ERRNO; + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; +@@ -547,6 +553,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ + + if (buflen < ms) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +@@ -607,6 +614,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + *h_errnop = NO_RECOVERY; + return ret; +diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c +index f554828d49..f8db27ae27 100644 +--- a/src/nss-systemd/nss-systemd.c ++++ b/src/nss-systemd/nss-systemd.c +@@ -210,6 +210,7 @@ enum nss_status _nss_systemd_getpwnam_r( + + l = strlen(name); + if (buflen < l+1) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -227,6 +228,7 @@ enum nss_status _nss_systemd_getpwnam_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -310,6 +312,7 @@ enum nss_status _nss_systemd_getpwuid_r( + + l = strlen(translated) + 1; + if (buflen < l) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -327,6 +330,7 @@ enum nss_status _nss_systemd_getpwuid_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -408,6 +412,7 @@ enum nss_status _nss_systemd_getgrnam_r( + + l = sizeof(char*) + strlen(name) + 1; + if (buflen < l) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -423,6 +428,7 @@ enum nss_status _nss_systemd_getgrnam_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -506,6 +512,7 @@ enum nss_status _nss_systemd_getgrgid_r( + + l = sizeof(char*) + strlen(translated) + 1; + if (buflen < l) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } +@@ -521,6 +528,7 @@ enum nss_status _nss_systemd_getgrgid_r( + return NSS_STATUS_SUCCESS; + + fail: ++ UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } +@@ -740,6 +748,7 @@ enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, siz + LIST_FOREACH(entries, p, getpwent_data.position) { + len = strlen(p->name) + 1; + if (buflen < len) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + ret = NSS_STATUS_TRYAGAIN; + goto finalize; +@@ -791,6 +800,7 @@ enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size + LIST_FOREACH(entries, p, getgrent_data.position) { + len = sizeof(char*) + strlen(p->name) + 1; + if (buflen < len) { ++ UNPROTECT_ERRNO; + *errnop = ERANGE; + ret = NSS_STATUS_TRYAGAIN; + goto finalize; diff --git a/SOURCES/0124-seccomp-reduce-logging-about-failure-to-add-syscall-.patch b/SOURCES/0124-seccomp-reduce-logging-about-failure-to-add-syscall-.patch new file mode 100644 index 0000000..a08a943 --- /dev/null +++ b/SOURCES/0124-seccomp-reduce-logging-about-failure-to-add-syscall-.patch @@ -0,0 +1,309 @@ +From da8ea9abbacf381513896a7064a1fa0067b3d549 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 24 Sep 2018 16:59:12 +0200 +Subject: [PATCH] seccomp: reduce logging about failure to add syscall to + seccomp + +Our logs are full of: +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call oldstat() / -10037, ignoring: Numerical argument out of domain +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call get_thread_area() / -10076, ignoring: Numerical argument out of domain +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call set_thread_area() / -10079, ignoring: Numerical argument out of domain +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call oldfstat() / -10034, ignoring: Numerical argument out of domain +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call oldolduname() / -10036, ignoring: Numerical argument out of domain +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call oldlstat() / -10035, ignoring: Numerical argument out of domain +Sep 19 09:22:10 autopkgtest systemd[690]: Failed to add rule for system call waitpid() / -10073, ignoring: Numerical argument out of domain +... +This is pointless and makes debug logs hard to read. Let's keep the logs +in test code, but disable it in nspawn and pid1. This is done through a function +parameter because those functions operate recursively and it's not possible to +make the caller to log meaningfully. + +There should be no functional change, except the skipped debug logs. + +(cherry-picked from commit b54f36c604472ffe08830ec4306fa2885b4a5424) + +Resolves: #1658691 +--- + src/core/execute.c | 6 ++-- + src/nspawn/nspawn-seccomp.c | 4 +-- + src/shared/seccomp-util.c | 57 ++++++++++++++++++++----------------- + src/shared/seccomp-util.h | 6 ++-- + src/test/test-seccomp.c | 16 +++++------ + 5 files changed, 47 insertions(+), 42 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 8ac69d1a0f..ffb92ddfc7 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -1415,7 +1415,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_ + return r; + } + +- return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_filter, action); ++ return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_filter, action, false); + } + + static int apply_syscall_archs(const Unit *u, const ExecContext *c) { +@@ -1498,7 +1498,7 @@ static int apply_protect_kernel_modules(const Unit *u, const ExecContext *c) { + if (skip_seccomp_unavailable(u, "ProtectKernelModules=")) + return 0; + +- return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM)); ++ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM), false); + } + + static int apply_private_devices(const Unit *u, const ExecContext *c) { +@@ -1513,7 +1513,7 @@ static int apply_private_devices(const Unit *u, const ExecContext *c) { + if (skip_seccomp_unavailable(u, "PrivateDevices=")) + return 0; + +- return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM)); ++ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM), false); + } + + static int apply_restrict_namespaces(const Unit *u, const ExecContext *c) { +diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c +index eb1964bb6d..b56c5b04a8 100644 +--- a/src/nspawn/nspawn-seccomp.c ++++ b/src/nspawn/nspawn-seccomp.c +@@ -148,7 +148,7 @@ static int seccomp_add_default_syscall_filter( + if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0) + continue; + +- r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist); ++ r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist, false); + if (r < 0) + /* If the system call is not known on this architecture, then that's fine, let's ignore it */ + log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", whitelist[i].name, seccomp_arch_to_string(arch)); +@@ -157,7 +157,7 @@ static int seccomp_add_default_syscall_filter( + } + + STRV_FOREACH(p, syscall_whitelist) { +- r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist); ++ r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist, false); + if (r < 0) + log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch)); + else +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index c433cb90dc..92910acf0e 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -857,11 +857,9 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name) { + return NULL; + } + +-static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude); +- +-int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude) { +- int r; ++static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude, bool log_missing); + ++int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude, bool log_missing) { + assert(seccomp); + assert(name); + +@@ -877,32 +875,36 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, + return -EINVAL; + } + +- r = seccomp_add_syscall_filter_set(seccomp, other, action, exclude); +- if (r < 0) +- return r; ++ return seccomp_add_syscall_filter_set(seccomp, other, action, exclude, log_missing); ++ + } else { +- int id; ++ int id, r; + + id = seccomp_syscall_resolve_name(name); + if (id == __NR_SCMP_ERROR) { +- log_debug("System call %s is not known, ignoring.", name); ++ if (log_missing) ++ log_debug("System call %s is not known, ignoring.", name); + return 0; + } + + r = seccomp_rule_add_exact(seccomp, action, id, 0); +- if (r < 0) ++ if (r < 0) { + /* If the system call is not known on this architecture, then that's fine, let's ignore it */ +- log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", name, id); +- } ++ if (log_missing) ++ log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", ++ name, id); ++ } + +- return 0; ++ return 0; ++ } + } + + static int seccomp_add_syscall_filter_set( + scmp_filter_ctx seccomp, + const SyscallFilterSet *set, + uint32_t action, +- char **exclude) { ++ char **exclude, ++ bool log_missing) { + + const char *sys; + int r; +@@ -911,7 +913,7 @@ static int seccomp_add_syscall_filter_set( + assert(set); + + NULSTR_FOREACH(sys, set->value) { +- r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude); ++ r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude, log_missing); + if (r < 0) + return r; + } +@@ -919,7 +921,7 @@ static int seccomp_add_syscall_filter_set( + return 0; + } + +-int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action) { ++int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action, bool log_missing) { + uint32_t arch; + int r; + +@@ -937,7 +939,7 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter + if (r < 0) + return r; + +- r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL); ++ r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL, log_missing); + if (r < 0) { + log_debug_errno(r, "Failed to add filter set, ignoring: %m"); + continue; +@@ -953,7 +955,7 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter + return 0; + } + +-int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action) { ++int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action, bool log_missing) { + uint32_t arch; + int r; + +@@ -966,7 +968,7 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + Iterator i; +- void *id, *val; ++ void *syscall_id, *val; + + log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + +@@ -974,20 +976,23 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u + if (r < 0) + return r; + +- HASHMAP_FOREACH_KEY(val, id, set, i) { ++ HASHMAP_FOREACH_KEY(val, syscall_id, set, i) { + uint32_t a = action; +- int e = PTR_TO_INT(val); ++ int id = PTR_TO_INT(syscall_id) - 1; ++ int error = PTR_TO_INT(val); + +- if (action != SCMP_ACT_ALLOW && e >= 0) +- a = SCMP_ACT_ERRNO(e); ++ if (action != SCMP_ACT_ALLOW && error >= 0) ++ a = SCMP_ACT_ERRNO(error); + +- r = seccomp_rule_add_exact(seccomp, a, PTR_TO_INT(id) - 1, 0); ++ r = seccomp_rule_add_exact(seccomp, a, id, 0); + if (r < 0) { + /* If the system call is not known on this architecture, then that's fine, let's ignore it */ + _cleanup_free_ char *n = NULL; + +- n = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); +- log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", strna(n), PTR_TO_INT(id) - 1); ++ n = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, id); ++ if (log_missing) ++ log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", ++ strna(n), id); + } + } + +diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h +index eac857afb9..d8a36c4e21 100644 +--- a/src/shared/seccomp-util.h ++++ b/src/shared/seccomp-util.h +@@ -58,10 +58,10 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name); + + int seccomp_filter_set_add(Hashmap *s, bool b, const SyscallFilterSet *set); + +-int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude); ++int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude, bool log_missing); + +-int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); +-int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action); ++int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action, bool log_missing); ++int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action, bool log_missing); + + typedef enum SeccompParseFlags { + SECCOMP_PARSE_INVERT = 1 << 0, +diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c +index d82cb5c1c5..d177515ac7 100644 +--- a/src/test/test-seccomp.c ++++ b/src/test/test-seccomp.c +@@ -104,11 +104,11 @@ static void test_filter_sets(void) { + if (pid == 0) { /* Child? */ + int fd; + +- /* if we look at the default set (or one that includes it), whitelist instead of blacklist */ ++ /* If we look at the default set (or one that includes it), whitelist instead of blacklist */ + if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_SYSTEM_SERVICE)) +- r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW); ++ r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW, true); + else +- r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN)); ++ r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN), true); + if (r < 0) + _exit(EXIT_FAILURE); + +@@ -515,7 +515,7 @@ static void test_load_syscall_filter_set_raw(void) { + assert_se(access("/", F_OK) >= 0); + assert_se(poll(NULL, 0, 0) == 0); + +- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL) >= 0); ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL, true) >= 0); + assert_se(access("/", F_OK) >= 0); + assert_se(poll(NULL, 0, 0) == 0); + +@@ -526,7 +526,7 @@ static void test_load_syscall_filter_set_raw(void) { + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(-1)) >= 0); + #endif + +- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN), true) >= 0); + + assert_se(access("/", F_OK) < 0); + assert_se(errno == EUCLEAN); +@@ -542,7 +542,7 @@ static void test_load_syscall_filter_set_raw(void) { + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(EILSEQ)) >= 0); + #endif + +- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN), true) >= 0); + + assert_se(access("/", F_OK) < 0); + assert_se(errno == EILSEQ); +@@ -558,7 +558,7 @@ static void test_load_syscall_filter_set_raw(void) { + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(-1)) >= 0); + #endif + +- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH), true) >= 0); + + assert_se(access("/", F_OK) < 0); + assert_se(errno == EILSEQ); +@@ -575,7 +575,7 @@ static void test_load_syscall_filter_set_raw(void) { + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(EILSEQ)) >= 0); + #endif + +- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH), true) >= 0); + + assert_se(access("/", F_OK) < 0); + assert_se(errno == EILSEQ); diff --git a/SOURCES/0125-format-table-when-duplicating-a-cell-also-copy-the-c.patch b/SOURCES/0125-format-table-when-duplicating-a-cell-also-copy-the-c.patch new file mode 100644 index 0000000..efd330b --- /dev/null +++ b/SOURCES/0125-format-table-when-duplicating-a-cell-also-copy-the-c.patch @@ -0,0 +1,25 @@ +From f18db51fffbcecc2d7a30b2317c8a7a43b613757 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 21:16:23 +0100 +Subject: [PATCH] format-table: when duplicating a cell, also copy the color + +(cherry picked from commit 13b0d4d7bdb674d0e51a6d595abd1e7bf2691bf9) + +Related: #1689832 +--- + src/basic/format-table.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 94e796d1ca..3429d9a071 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -414,6 +414,8 @@ static int table_dedup_cell(Table *t, TableCell *cell) { + if (!nd) + return -ENOMEM; + ++ nd->color = od->color; ++ + table_data_unref(od); + t->data[i] = nd; + diff --git a/SOURCES/0126-format-table-optionally-make-specific-cells-clickabl.patch b/SOURCES/0126-format-table-optionally-make-specific-cells-clickabl.patch new file mode 100644 index 0000000..b4fdf1f --- /dev/null +++ b/SOURCES/0126-format-table-optionally-make-specific-cells-clickabl.patch @@ -0,0 +1,203 @@ +From 4ffba0dd993bc461df18fcf59591fc71ab6e6cc8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 6 Nov 2018 12:06:14 +0100 +Subject: [PATCH] format-table: optionally make specific cells clickable links + +(cherry picked from commit 165ca5663e9859083c70d793a6b4aa4f3b2af24c) + +Related: #1689832 +--- + src/basic/format-table.c | 79 +++++++++++++++++++++++++++++++++++----- + src/basic/format-table.h | 1 + + 2 files changed, 71 insertions(+), 9 deletions(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 3429d9a071..ac5d66eda2 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -9,6 +9,7 @@ + #include "gunicode.h" + #include "pager.h" + #include "parse-util.h" ++#include "pretty-print.h" + #include "string-util.h" + #include "terminal-util.h" + #include "time-util.h" +@@ -58,6 +59,7 @@ typedef struct TableData { + unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */ + + const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */ ++ char *url; /* A URL to use for a clickable hyperlink */ + char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */ + + union { +@@ -182,6 +184,8 @@ static TableData *table_data_unref(TableData *d) { + return NULL; + + free(d->formatted); ++ free(d->url); ++ + return mfree(d); + } + +@@ -392,6 +396,7 @@ int table_dup_cell(Table *t, TableCell *cell) { + } + + static int table_dedup_cell(Table *t, TableCell *cell) { ++ _cleanup_free_ char *curl = NULL; + TableData *nd, *od; + size_t i; + +@@ -410,11 +415,25 @@ static int table_dedup_cell(Table *t, TableCell *cell) { + + assert(od->n_ref > 1); + +- nd = table_data_new(od->type, od->data, od->minimum_width, od->maximum_width, od->weight, od->align_percent, od->ellipsize_percent); ++ if (od->url) { ++ curl = strdup(od->url); ++ if (!curl) ++ return -ENOMEM; ++ } ++ ++ nd = table_data_new( ++ od->type, ++ od->data, ++ od->minimum_width, ++ od->maximum_width, ++ od->weight, ++ od->align_percent, ++ od->ellipsize_percent); + if (!nd) + return -ENOMEM; + + nd->color = od->color; ++ nd->url = TAKE_PTR(curl); + + table_data_unref(od); + t->data[i] = nd; +@@ -542,6 +561,26 @@ int table_set_color(Table *t, TableCell *cell, const char *color) { + return 0; + } + ++int table_set_url(Table *t, TableCell *cell, const char *url) { ++ _cleanup_free_ char *copy = NULL; ++ int r; ++ ++ assert(t); ++ assert(cell); ++ ++ if (url) { ++ copy = strdup(url); ++ if (!copy) ++ return -ENOMEM; ++ } ++ ++ r = table_dedup_cell(t, cell); ++ if (r < 0) ++ return r; ++ ++ return free_and_replace(table_get_data(t, cell)->url, copy); ++} ++ + int table_add_many_internal(Table *t, TableDataType first_type, ...) { + TableDataType type; + va_list ap; +@@ -884,11 +923,13 @@ static int table_data_requested_width(TableData *d, size_t *ret) { + return 0; + } + +-static char *align_string_mem(const char *str, size_t new_length, unsigned percent) { +- size_t w = 0, space, lspace, old_length; ++static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) { ++ size_t w = 0, space, lspace, old_length, clickable_length; ++ _cleanup_free_ char *clickable = NULL; + const char *p; + char *ret; + size_t i; ++ int r; + + /* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */ + +@@ -897,6 +938,15 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce + + old_length = strlen(str); + ++ if (url) { ++ r = terminal_urlify(url, str, &clickable); ++ if (r < 0) ++ return NULL; ++ ++ clickable_length = strlen(clickable); ++ } else ++ clickable_length = old_length; ++ + /* Determine current width on screen */ + p = str; + while (p < str + old_length) { +@@ -913,23 +963,23 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce + + /* Already wider than the target, if so, don't do anything */ + if (w >= new_length) +- return strndup(str, old_length); ++ return clickable ? TAKE_PTR(clickable) : strdup(str); + + /* How much spaces shall we add? An how much on the left side? */ + space = new_length - w; + lspace = space * percent / 100U; + +- ret = new(char, space + old_length + 1); ++ ret = new(char, space + clickable_length + 1); + if (!ret) + return NULL; + + for (i = 0; i < lspace; i++) + ret[i] = ' '; +- memcpy(ret + lspace, str, old_length); +- for (i = lspace + old_length; i < space + old_length; i++) ++ memcpy(ret + lspace, clickable ?: str, clickable_length); ++ for (i = lspace + clickable_length; i < space + clickable_length; i++) + ret[i] = ' '; + +- ret[space + old_length] = 0; ++ ret[space + clickable_length] = 0; + return ret; + } + +@@ -1184,13 +1234,24 @@ int table_print(Table *t, FILE *f) { + } else if (l < width[j]) { + /* Field is shorter than allocated space. Let's align with spaces */ + +- buffer = align_string_mem(field, width[j], d->align_percent); ++ buffer = align_string_mem(field, d->url, width[j], d->align_percent); + if (!buffer) + return -ENOMEM; + + field = buffer; + } + ++ if (l >= width[j] && d->url) { ++ _cleanup_free_ char *clickable = NULL; ++ ++ r = terminal_urlify(d->url, field, &clickable); ++ if (r < 0) ++ return r; ++ ++ free_and_replace(buffer, clickable); ++ field = buffer; ++ } ++ + if (j > 0) + fputc(' ', f); /* column separator */ + +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index 6dc2d16052..9978a8baf2 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -42,6 +42,7 @@ int table_set_weight(Table *t, TableCell *cell, unsigned weight); + int table_set_align_percent(Table *t, TableCell *cell, unsigned percent); + int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent); + int table_set_color(Table *t, TableCell *cell, const char *color); ++int table_set_url(Table *t, TableCell *cell, const char *color); + + int table_add_many_internal(Table *t, TableDataType first_type, ...); + #define table_add_many(t, ...) table_add_many_internal(t, __VA_ARGS__, _TABLE_DATA_TYPE_MAX) diff --git a/SOURCES/0127-format-table-before-outputting-a-color-check-if-colo.patch b/SOURCES/0127-format-table-before-outputting-a-color-check-if-colo.patch new file mode 100644 index 0000000..8a57493 --- /dev/null +++ b/SOURCES/0127-format-table-before-outputting-a-color-check-if-colo.patch @@ -0,0 +1,40 @@ +From 02b72c15a53f694f65a377206fba478db91432cd Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 6 Nov 2018 18:37:21 +0100 +Subject: [PATCH] format-table: before outputting a color, check if colors are + available + +This is in many cases redundant, as a similar check is done by various +callers already, but in other cases (where we read the color from a +static table for example), it's nice to let the color check be done by +the table code itself, and since it doesn't hurt in the other cases just +do it again. + +(cherry picked from commit a22318e55492af721879d8692ed039144696bb08) + +Related: #1689832 +--- + src/basic/format-table.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index ac5d66eda2..17be7285cd 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -1255,13 +1255,13 @@ int table_print(Table *t, FILE *f) { + if (j > 0) + fputc(' ', f); /* column separator */ + +- if (d->color) ++ if (d->color && colors_enabled()) + fputs(d->color, f); + + fputs(field, f); + +- if (d->color) +- fputs(ansi_normal(), f); ++ if (d->color && colors_enabled()) ++ fputs(ANSI_NORMAL, f); + } + + fputc('\n', f); diff --git a/SOURCES/0128-format-table-add-option-to-store-format-percent-and-.patch b/SOURCES/0128-format-table-add-option-to-store-format-percent-and-.patch new file mode 100644 index 0000000..7e5e8ac --- /dev/null +++ b/SOURCES/0128-format-table-add-option-to-store-format-percent-and-.patch @@ -0,0 +1,147 @@ +From 85ccda9eabb6b89e644cedd9faafb5dbe97e8341 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 7 Nov 2018 15:25:51 +0100 +Subject: [PATCH] format-table: add option to store/format percent and uint64_t + values in cells + +(cherry picked from commit a4661181fa702a8bff4644210ba7ea14bea51a4a) + +Related: #1689832 +--- + src/basic/format-table.c | 48 ++++++++++++++++++++++++++++++++++++++++ + src/basic/format-table.h | 2 ++ + src/basic/macro.h | 9 ++++++++ + 3 files changed, 59 insertions(+) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 17be7285cd..64b9eb4108 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -70,6 +70,8 @@ typedef struct TableData { + uint64_t size; + char string[0]; + uint32_t uint32; ++ uint64_t uint64; ++ int percent; /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */ + /* … add more here as we start supporting more cell data types … */ + }; + } TableData; +@@ -235,11 +237,15 @@ static size_t table_data_size(TableDataType type, const void *data) { + return sizeof(usec_t); + + case TABLE_SIZE: ++ case TABLE_UINT64: + return sizeof(uint64_t); + + case TABLE_UINT32: + return sizeof(uint32_t); + ++ case TABLE_PERCENT: ++ return sizeof(int); ++ + default: + assert_not_reached("Uh? Unexpected cell type"); + } +@@ -599,6 +605,8 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { + uint64_t size; + usec_t usec; + uint32_t uint32; ++ uint64_t uint64; ++ int percent; + bool b; + } buffer; + +@@ -633,6 +641,16 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) { + data = &buffer.uint32; + break; + ++ case TABLE_UINT64: ++ buffer.uint64 = va_arg(ap, uint64_t); ++ data = &buffer.uint64; ++ break; ++ ++ case TABLE_PERCENT: ++ buffer.percent = va_arg(ap, int); ++ data = &buffer.percent; ++ break; ++ + case _TABLE_DATA_TYPE_MAX: + /* Used as end marker */ + va_end(ap); +@@ -772,6 +790,12 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t + return 1; + return 0; + ++ case TABLE_UINT64: ++ return CMP(a->uint64, b->uint64); ++ ++ case TABLE_PERCENT: ++ return CMP(a->percent, b->percent); ++ + default: + ; + } +@@ -894,6 +918,30 @@ static const char *table_data_format(TableData *d) { + break; + } + ++ case TABLE_UINT64: { ++ _cleanup_free_ char *p; ++ ++ p = new(char, DECIMAL_STR_WIDTH(d->uint64) + 1); ++ if (!p) ++ return NULL; ++ ++ sprintf(p, "%" PRIu64, d->uint64); ++ d->formatted = TAKE_PTR(p); ++ break; ++ } ++ ++ case TABLE_PERCENT: { ++ _cleanup_free_ char *p; ++ ++ p = new(char, DECIMAL_STR_WIDTH(d->percent) + 2); ++ if (!p) ++ return NULL; ++ ++ sprintf(p, "%i%%" , d->percent); ++ d->formatted = TAKE_PTR(p); ++ break; ++ } ++ + default: + assert_not_reached("Unexpected type?"); + } +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index 9978a8baf2..2db2084062 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -15,6 +15,8 @@ typedef enum TableDataType { + TABLE_TIMESPAN, + TABLE_SIZE, + TABLE_UINT32, ++ TABLE_UINT64, ++ TABLE_PERCENT, + _TABLE_DATA_TYPE_MAX, + _TABLE_DATA_TYPE_INVALID = -1, + } TableDataType; +diff --git a/src/basic/macro.h b/src/basic/macro.h +index d1365f7058..79ab02b27a 100644 +--- a/src/basic/macro.h ++++ b/src/basic/macro.h +@@ -222,6 +222,15 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { + UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \ + }) + ++#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) ++#define __CMP(aq, a, bq, b) \ ++ ({ \ ++ const typeof(a) UNIQ_T(A, aq) = (a); \ ++ const typeof(b) UNIQ_T(B, bq) = (b); \ ++ UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ ++ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ ++ }) ++ + #undef CLAMP + #define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) + #define __CLAMP(xq, x, lowq, low, highq, high) \ diff --git a/SOURCES/0129-format-table-optionally-allow-reversing-the-sort-ord.patch b/SOURCES/0129-format-table-optionally-allow-reversing-the-sort-ord.patch new file mode 100644 index 0000000..53d0100 --- /dev/null +++ b/SOURCES/0129-format-table-optionally-allow-reversing-the-sort-ord.patch @@ -0,0 +1,77 @@ +From 75130d739d206715a09619c7b7bf62613dab1d2b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 7 Nov 2018 17:41:32 +0100 +Subject: [PATCH] format-table: optionally allow reversing the sort order for a + column + +(cherry picked from commit a2c73e2d3823e878de7a7ee193631108c5fc5be0) + +Related: #1689832 +--- + src/basic/format-table.c | 22 +++++++++++++++++++++- + src/basic/format-table.h | 1 + + 2 files changed, 22 insertions(+), 1 deletion(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 64b9eb4108..a3ff527e91 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -107,6 +107,8 @@ struct Table { + + size_t *sort_map; /* The columns to order rows by, in order of preference. */ + size_t n_sort_map; ++ ++ bool *reverse_map; + }; + + Table *table_new_raw(size_t n_columns) { +@@ -215,6 +217,7 @@ Table *table_unref(Table *t) { + free(t->data); + free(t->display_map); + free(t->sort_map); ++ free(t->reverse_map); + + return mfree(t); + } +@@ -836,7 +839,7 @@ static int table_data_compare(const void *x, const void *y, void *userdata) { + + r = cell_data_compare(d, *a, dd, *b); + if (r != 0) +- return r; ++ return t->reverse_map && t->reverse_map[t->sort_map[i]] ? -r : r; + } + + /* Order identical lines by the order there were originally added in */ +@@ -1356,3 +1359,20 @@ size_t table_get_columns(Table *t) { + assert(t->n_columns > 0); + return t->n_columns; + } ++ ++int table_set_reverse(Table *t, size_t column, bool b) { ++ assert(t); ++ assert(column < t->n_columns); ++ ++ if (!t->reverse_map) { ++ if (!b) ++ return 0; ++ ++ t->reverse_map = new0(bool, t->n_columns); ++ if (!t->reverse_map) ++ return -ENOMEM; ++ } ++ ++ t->reverse_map[column] = b; ++ return 0; ++} +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index 2db2084062..07cb2351cb 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -53,6 +53,7 @@ void table_set_header(Table *table, bool b); + void table_set_width(Table *t, size_t width); + int table_set_display(Table *t, size_t first_column, ...); + int table_set_sort(Table *t, size_t first_column, ...); ++int table_set_reverse(Table *t, size_t column, bool b); + + int table_print(Table *t, FILE *f); + int table_format(Table *t, char **ret); diff --git a/SOURCES/0130-format-table-add-table_update-to-update-existing-ent.patch b/SOURCES/0130-format-table-add-table_update-to-update-existing-ent.patch new file mode 100644 index 0000000..258fca3 --- /dev/null +++ b/SOURCES/0130-format-table-add-table_update-to-update-existing-ent.patch @@ -0,0 +1,77 @@ +From a53afb337985f8f1c7fe8f620fe30cec87f554d5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 21:17:47 +0100 +Subject: [PATCH] format-table: add table_update() to update existing entries + +(cherry picked from commit 27e730e6d0a7709c17ccef170f10846e92dca2a0) + +Related: #1689832 +--- + src/basic/format-table.c | 40 ++++++++++++++++++++++++++++++++++++++++ + src/basic/format-table.h | 2 ++ + 2 files changed, 42 insertions(+) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index a3ff527e91..302642d748 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -590,6 +590,46 @@ int table_set_url(Table *t, TableCell *cell, const char *url) { + return free_and_replace(table_get_data(t, cell)->url, copy); + } + ++int table_update(Table *t, TableCell *cell, TableDataType type, const void *data) { ++ _cleanup_free_ char *curl = NULL; ++ TableData *nd, *od; ++ size_t i; ++ ++ assert(t); ++ assert(cell); ++ ++ i = TABLE_CELL_TO_INDEX(cell); ++ if (i >= t->n_cells) ++ return -ENXIO; ++ ++ assert_se(od = t->data[i]); ++ ++ if (od->url) { ++ curl = strdup(od->url); ++ if (!curl) ++ return -ENOMEM; ++ } ++ ++ nd = table_data_new( ++ type, ++ data, ++ od->minimum_width, ++ od->maximum_width, ++ od->weight, ++ od->align_percent, ++ od->ellipsize_percent); ++ if (!nd) ++ return -ENOMEM; ++ ++ nd->color = od->color; ++ nd->url = TAKE_PTR(curl); ++ ++ table_data_unref(od); ++ t->data[i] = nd; ++ ++ return 0; ++} ++ + int table_add_many_internal(Table *t, TableDataType first_type, ...) { + TableDataType type; + va_list ap; +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index 07cb2351cb..4273c8c49b 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -46,6 +46,8 @@ int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent); + int table_set_color(Table *t, TableCell *cell, const char *color); + int table_set_url(Table *t, TableCell *cell, const char *color); + ++int table_update(Table *t, TableCell *cell, TableDataType type, const void *data); ++ + int table_add_many_internal(Table *t, TableDataType first_type, ...); + #define table_add_many(t, ...) table_add_many_internal(t, __VA_ARGS__, _TABLE_DATA_TYPE_MAX) + diff --git a/SOURCES/0131-format-table-add-an-API-for-getting-the-cell-at-a-sp.patch b/SOURCES/0131-format-table-add-an-API-for-getting-the-cell-at-a-sp.patch new file mode 100644 index 0000000..1e6698e --- /dev/null +++ b/SOURCES/0131-format-table-add-an-API-for-getting-the-cell-at-a-sp.patch @@ -0,0 +1,47 @@ +From ecc27a3bf5935cebc63aa4e3b650861d1f0ef97f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 21:21:09 +0100 +Subject: [PATCH] format-table: add an API for getting the cell at a specific + row/column + +(cherry picked from commit 9314ead7853a1479fc60eb2ae7e3d0a77b7eba7c) + +Related: #1689832 +--- + src/basic/format-table.c | 15 +++++++++++++++ + src/basic/format-table.h | 2 ++ + 2 files changed, 17 insertions(+) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 302642d748..809a4be284 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -1416,3 +1416,18 @@ int table_set_reverse(Table *t, size_t column, bool b) { + t->reverse_map[column] = b; + return 0; + } ++ ++TableCell *table_get_cell(Table *t, size_t row, size_t column) { ++ size_t i; ++ ++ assert(t); ++ ++ if (column >= t->n_columns) ++ return NULL; ++ ++ i = row * t->n_columns + column; ++ if (i >= t->n_cells) ++ return NULL; ++ ++ return TABLE_INDEX_TO_CELL(i); ++} +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index 4273c8c49b..40fea79f78 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -66,3 +66,5 @@ static inline TableCell* TABLE_HEADER_CELL(size_t i) { + + size_t table_get_rows(Table *t); + size_t table_get_columns(Table *t); ++ ++TableCell *table_get_cell(Table *t, size_t row, size_t column); diff --git a/SOURCES/0132-format-table-always-underline-header-line.patch b/SOURCES/0132-format-table-always-underline-header-line.patch new file mode 100644 index 0000000..9eb9495 --- /dev/null +++ b/SOURCES/0132-format-table-always-underline-header-line.patch @@ -0,0 +1,41 @@ +From cb35bd029dedf39b7be4945d57176a5b1aa03da9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 21:39:28 +0100 +Subject: [PATCH] format-table: always underline header line + +(cherry picked from commit 30d98de00c68440ff4d77d851b4b3323c34027da) + +Related: #1689832 +--- + src/basic/format-table.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 809a4be284..3fcb97475c 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -1343,15 +1343,22 @@ int table_print(Table *t, FILE *f) { + field = buffer; + } + ++ if (row == t->data) /* underline header line fully, including the column separator */ ++ fputs(ansi_underline(), f); ++ + if (j > 0) + fputc(' ', f); /* column separator */ + +- if (d->color && colors_enabled()) ++ if (d->color && colors_enabled()) { ++ if (row == t->data) /* first undo header underliner */ ++ fputs(ANSI_NORMAL, f); ++ + fputs(d->color, f); ++ } + + fputs(field, f); + +- if (d->color && colors_enabled()) ++ if (colors_enabled() && (d->color || row == t->data)) + fputs(ANSI_NORMAL, f); + } + diff --git a/SOURCES/0133-format-table-add-calls-to-query-the-data-in-a-specif.patch b/SOURCES/0133-format-table-add-calls-to-query-the-data-in-a-specif.patch new file mode 100644 index 0000000..669a19c --- /dev/null +++ b/SOURCES/0133-format-table-add-calls-to-query-the-data-in-a-specif.patch @@ -0,0 +1,54 @@ +From c4f60df10a2716c94d30462a118f60d916d537a2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 9 Nov 2018 11:38:12 +0100 +Subject: [PATCH] format-table: add calls to query the data in a specific cell + +(cherry picked from commit 62d99b39709f903f8a66a9aae757deb5546a53eb) + +Related: #1689832 +--- + src/basic/format-table.c | 22 ++++++++++++++++++++++ + src/basic/format-table.h | 3 +++ + 2 files changed, 25 insertions(+) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 3fcb97475c..0a1777e9a2 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -1438,3 +1438,25 @@ TableCell *table_get_cell(Table *t, size_t row, size_t column) { + + return TABLE_INDEX_TO_CELL(i); + } ++ ++const void *table_get(Table *t, TableCell *cell) { ++ TableData *d; ++ ++ assert(t); ++ ++ d = table_get_data(t, cell); ++ if (!d) ++ return NULL; ++ ++ return d->data; ++} ++ ++const void* table_get_at(Table *t, size_t row, size_t column) { ++ TableCell *cell; ++ ++ cell = table_get_cell(t, row, column); ++ if (!cell) ++ return NULL; ++ ++ return table_get(t, cell); ++} +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index 40fea79f78..a2bb2e0846 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -68,3 +68,6 @@ size_t table_get_rows(Table *t); + size_t table_get_columns(Table *t); + + TableCell *table_get_cell(Table *t, size_t row, size_t column); ++ ++const void *table_get(Table *t, TableCell *cell); ++const void *table_get_at(Table *t, size_t row, size_t column); diff --git a/SOURCES/0134-format-table-make-sure-we-never-call-memcmp-with-NUL.patch b/SOURCES/0134-format-table-make-sure-we-never-call-memcmp-with-NUL.patch new file mode 100644 index 0000000..8b022b0 --- /dev/null +++ b/SOURCES/0134-format-table-make-sure-we-never-call-memcmp-with-NUL.patch @@ -0,0 +1,47 @@ +From 4339aa85279d268a671a656f142445559be771b7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 14 Nov 2018 18:39:37 +0100 +Subject: [PATCH] format-table: make sure we never call memcmp() with NULL + parameters + +(cherry picked from commit 88db94fa57c9a5b1a0b926c49d3624fc84c88090) + +Related: #1689832 +--- + src/basic/format-table.c | 2 +- + src/basic/util.h | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 0a1777e9a2..5517adad82 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -291,7 +291,7 @@ static bool table_data_matches( + if (k != l) + return false; + +- return memcmp(data, d->data, l) == 0; ++ return memcmp_safe(data, d->data, l) == 0; + } + + static TableData *table_data_new( +diff --git a/src/basic/util.h b/src/basic/util.h +index 27b5a09782..b68ef25ed8 100644 +--- a/src/basic/util.h ++++ b/src/basic/util.h +@@ -123,6 +123,15 @@ static inline void memcpy_safe(void *dst, const void *src, size_t n) { + memcpy(dst, src, n); + } + ++/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ ++static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { ++ if (n == 0) ++ return 0; ++ assert(s1); ++ assert(s2); ++ return memcmp(s1, s2, n); ++} ++ + int on_ac_power(void); + + #define memzero(x,l) (memset((x), 0, (l))) diff --git a/SOURCES/0135-format-table-use-right-field-for-display.patch b/SOURCES/0135-format-table-use-right-field-for-display.patch new file mode 100644 index 0000000..eba9ee4 --- /dev/null +++ b/SOURCES/0135-format-table-use-right-field-for-display.patch @@ -0,0 +1,29 @@ +From d4c1d109a883fbc5695cf10c79e61bedbe788829 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 3 Dec 2018 20:28:15 +0100 +Subject: [PATCH] format-table: use right field for display + +Since .timespan and .timestamp are unionized on top of each other this +doesn't actually matter, but it is still more correct to address it +under it's correct name. + +(cherry picked from commit c93d372d7ceda0b080487fa35d3638ef3d8507cb) + +Related: #1689832 +--- + src/basic/format-table.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 5517adad82..cfb8aadbda 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -928,7 +928,7 @@ static const char *table_data_format(TableData *d) { + if (!p) + return NULL; + +- if (!format_timespan(p, FORMAT_TIMESPAN_MAX, d->timestamp, 0)) ++ if (!format_timespan(p, FORMAT_TIMESPAN_MAX, d->timespan, 0)) + return "n/a"; + + d->formatted = TAKE_PTR(p); diff --git a/SOURCES/0136-format-table-add-option-to-uppercase-cells-on-displa.patch b/SOURCES/0136-format-table-add-option-to-uppercase-cells-on-displa.patch new file mode 100644 index 0000000..0c0523c --- /dev/null +++ b/SOURCES/0136-format-table-add-option-to-uppercase-cells-on-displa.patch @@ -0,0 +1,165 @@ +From 1d3c6e3c0937ac56a51594a3b6908a801fa9ac5c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 3 Dec 2018 21:36:26 +0100 +Subject: [PATCH] format-table: add option to uppercase cells on display + +This adds a per-cell option for uppercasing displayed strings. +Implicitly turn this on for the header row. The fact that we format the +table header in uppercase is a formatting thing after all, hence should +be applied by the formatter, i.e. the table display code. + +Moreover, this provides us with the benefit that we can more nicely +reuse the specified table headers as JSON field names, like we already +do: json field names are usually not uppercase. + +(cherry picked from commit 359abf6dd05aa6bca3438e9c969ed904bd3d447d) + +Related: #1689832 +--- + src/basic/format-table.c | 62 ++++++++++++++++++++++++++++++++-------- + src/basic/format-table.h | 1 + + 2 files changed, 51 insertions(+), 12 deletions(-) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index cfb8aadbda..fe2201ee5f 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -1,5 +1,6 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + ++#include + #include + + #include "alloc-util.h" +@@ -58,6 +59,8 @@ typedef struct TableData { + unsigned ellipsize_percent; /* 0 … 100, where to place the ellipsis when compression is needed */ + unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */ + ++ bool uppercase; /* Uppercase string on display */ ++ + const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */ + char *url; /* A URL to use for a clickable hyperlink */ + char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */ +@@ -132,6 +135,7 @@ Table *table_new_raw(size_t n_columns) { + Table *table_new_internal(const char *first_header, ...) { + _cleanup_(table_unrefp) Table *t = NULL; + size_t n_columns = 1; ++ const char *h; + va_list ap; + int r; + +@@ -139,8 +143,6 @@ Table *table_new_internal(const char *first_header, ...) { + + va_start(ap, first_header); + for (;;) { +- const char *h; +- + h = va_arg(ap, const char*); + if (!h) + break; +@@ -153,19 +155,18 @@ Table *table_new_internal(const char *first_header, ...) { + if (!t) + return NULL; + +- r = table_add_cell(t, NULL, TABLE_STRING, first_header); +- if (r < 0) +- return NULL; +- + va_start(ap, first_header); +- for (;;) { +- const char *h; ++ for (h = first_header; h; h = va_arg(ap, const char*)) { ++ TableCell *cell; + +- h = va_arg(ap, const char*); +- if (!h) +- break; ++ r = table_add_cell(t, &cell, TABLE_STRING, h); ++ if (r < 0) { ++ va_end(ap); ++ return NULL; ++ } + +- r = table_add_cell(t, NULL, TABLE_STRING, h); ++ /* Make the table header uppercase */ ++ r = table_set_uppercase(t, cell, true); + if (r < 0) { + va_end(ap); + return NULL; +@@ -443,6 +444,7 @@ static int table_dedup_cell(Table *t, TableCell *cell) { + + nd->color = od->color; + nd->url = TAKE_PTR(curl); ++ nd->uppercase = od->uppercase; + + table_data_unref(od); + t->data[i] = nd; +@@ -590,6 +592,27 @@ int table_set_url(Table *t, TableCell *cell, const char *url) { + return free_and_replace(table_get_data(t, cell)->url, copy); + } + ++int table_set_uppercase(Table *t, TableCell *cell, bool b) { ++ TableData *d; ++ int r; ++ ++ assert(t); ++ assert(cell); ++ ++ r = table_dedup_cell(t, cell); ++ if (r < 0) ++ return r; ++ ++ assert_se(d = table_get_data(t, cell)); ++ ++ if (d->uppercase == b) ++ return 0; ++ ++ d->formatted = mfree(d->formatted); ++ d->uppercase = b; ++ return 1; ++} ++ + int table_update(Table *t, TableCell *cell, TableDataType type, const void *data) { + _cleanup_free_ char *curl = NULL; + TableData *nd, *od; +@@ -623,6 +646,7 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data + + nd->color = od->color; + nd->url = TAKE_PTR(curl); ++ nd->uppercase = od->uppercase; + + table_data_unref(od); + t->data[i] = nd; +@@ -902,6 +926,20 @@ static const char *table_data_format(TableData *d) { + return ""; + + case TABLE_STRING: ++ if (d->uppercase) { ++ char *p, *q; ++ ++ d->formatted = new(char, strlen(d->string) + 1); ++ if (!d->formatted) ++ return NULL; ++ ++ for (p = d->string, q = d->formatted; *p; p++, q++) ++ *q = (char) toupper((unsigned char) *p); ++ *q = 0; ++ ++ return d->formatted; ++ } ++ + return d->string; + + case TABLE_BOOLEAN: +diff --git a/src/basic/format-table.h b/src/basic/format-table.h +index a2bb2e0846..5a076b5383 100644 +--- a/src/basic/format-table.h ++++ b/src/basic/format-table.h +@@ -45,6 +45,7 @@ int table_set_align_percent(Table *t, TableCell *cell, unsigned percent); + int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent); + int table_set_color(Table *t, TableCell *cell, const char *color); + int table_set_url(Table *t, TableCell *cell, const char *color); ++int table_set_uppercase(Table *t, TableCell *cell, bool b); + + int table_update(Table *t, TableCell *cell, TableDataType type, const void *data); + diff --git a/SOURCES/0137-format-table-never-try-to-reuse-cells-that-have-colo.patch b/SOURCES/0137-format-table-never-try-to-reuse-cells-that-have-colo.patch new file mode 100644 index 0000000..c1200ce --- /dev/null +++ b/SOURCES/0137-format-table-never-try-to-reuse-cells-that-have-colo.patch @@ -0,0 +1,36 @@ +From a064e883c7cc28b8d561a7fea56b139d8e7d2286 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 3 Dec 2018 21:39:39 +0100 +Subject: [PATCH] format-table: never try to reuse cells that have + color/url/uppercase set + +The table cell reusing code is supposed to be an internal memory +optimization, and not more. This means behaviour should be the same as +if we wouldn't reuse cells. + +(cherry picked from commit 94a80afed42a8b36d69a229bf44ba690f8f59a78) + +Related: #1689832 +--- + src/basic/format-table.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index fe2201ee5f..844b92f41c 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -286,6 +286,14 @@ static bool table_data_matches( + if (d->ellipsize_percent != ellipsize_percent) + return false; + ++ /* If a color/url/uppercase flag is set, refuse to merge */ ++ if (d->color) ++ return false; ++ if (d->url) ++ return false; ++ if (d->uppercase) ++ return false; ++ + k = table_data_size(type, data); + l = table_data_size(d->type, d->data); + diff --git a/SOURCES/0138-locale-util-add-logic-to-output-smiley-emojis-at-var.patch b/SOURCES/0138-locale-util-add-logic-to-output-smiley-emojis-at-var.patch new file mode 100644 index 0000000..6205811 --- /dev/null +++ b/SOURCES/0138-locale-util-add-logic-to-output-smiley-emojis-at-var.patch @@ -0,0 +1,207 @@ +From bc00d9db41a87b7a4b92c46f277e62ad58768420 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 6 Nov 2018 17:59:58 +0100 +Subject: [PATCH] locale-util: add logic to output smiley emojis at various + happiness levels + +(cherry picked from commit 5f1b0cc6d064f7847982e7b680cab3d080aef52e) + +Conflicts: + doc/ENVIRONMENT.md + src/basic/locale-util.c + src/basic/locale-util.h + src/test/test-locale-util.c + +Related: #1689832 +--- + doc/ENVIRONMENT.md | 5 +++ + src/basic/locale-util.c | 81 ++++++++++++++++++++++++++++--------- + src/basic/locale-util.h | 12 ++++++ + src/test/test-locale-util.c | 12 +++++- + 4 files changed, 90 insertions(+), 20 deletions(-) + +diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md +index 85d26fe28c..1e648be640 100644 +--- a/doc/ENVIRONMENT.md ++++ b/doc/ENVIRONMENT.md +@@ -37,6 +37,11 @@ All tools: + useful for debugging, in order to test generators and other code against + specific kernel command lines. + ++* `$SYSTEMD_EMOJI=0` — if set, tools such as "systemd-analyze security" will ++ not output graphical smiley emojis, but ASCII alternatives instead. Note that ++ this only controls use of Unicode emoji glyphs, and has no effect on other ++ Unicode glyphs. ++ + systemctl: + + * `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus +diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c +index 3ad352f22f..7cd143ea6f 100644 +--- a/src/basic/locale-util.c ++++ b/src/basic/locale-util.c +@@ -16,6 +16,7 @@ + + #include "def.h" + #include "dirent-util.h" ++#include "env-util.h" + #include "fd-util.h" + #include "hashmap.h" + #include "locale-util.h" +@@ -347,6 +348,24 @@ bool keymap_is_valid(const char *name) { + return true; + } + ++static bool emoji_enabled(void) { ++ static int cached_emoji_enabled = -1; ++ ++ if (cached_emoji_enabled < 0) { ++ int val; ++ ++ val = getenv_bool("SYSTEMD_EMOJI"); ++ if (val < 0) ++ cached_emoji_enabled = ++ is_locale_utf8() && ++ !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux"); ++ else ++ cached_emoji_enabled = val; ++ } ++ ++ return cached_emoji_enabled; ++} ++ + const char *special_glyph(SpecialGlyph code) { + + /* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be +@@ -359,32 +378,56 @@ const char *special_glyph(SpecialGlyph code) { + static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { + /* ASCII fallback */ + [false] = { +- [TREE_VERTICAL] = "| ", +- [TREE_BRANCH] = "|-", +- [TREE_RIGHT] = "`-", +- [TREE_SPACE] = " ", +- [TRIANGULAR_BULLET] = ">", +- [BLACK_CIRCLE] = "*", +- [ARROW] = "->", +- [MDASH] = "-", +- [ELLIPSIS] = "..." ++ [TREE_VERTICAL] = "| ", ++ [TREE_BRANCH] = "|-", ++ [TREE_RIGHT] = "`-", ++ [TREE_SPACE] = " ", ++ [TRIANGULAR_BULLET] = ">", ++ [BLACK_CIRCLE] = "*", ++ [BULLET] = "*", ++ [ARROW] = "->", ++ [MDASH] = "-", ++ [ELLIPSIS] = "...", ++ [MU] = "u", ++ [CHECK_MARK] = "+", ++ [CROSS_MARK] = "-", ++ [ECSTATIC_SMILEY] = ":-]", ++ [HAPPY_SMILEY] = ":-}", ++ [SLIGHTLY_HAPPY_SMILEY] = ":-)", ++ [NEUTRAL_SMILEY] = ":-|", ++ [SLIGHTLY_UNHAPPY_SMILEY] = ":-(", ++ [UNHAPPY_SMILEY] = ":-{️", ++ [DEPRESSED_SMILEY] = ":-[", + }, + + /* UTF-8 */ + [true] = { +- [TREE_VERTICAL] = "\342\224\202 ", /* │ */ +- [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ +- [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ +- [TREE_SPACE] = " ", /* */ +- [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ +- [BLACK_CIRCLE] = "\342\227\217", /* ● */ +- [ARROW] = "\342\206\222", /* → */ +- [MDASH] = "\342\200\223", /* – */ +- [ELLIPSIS] = "\342\200\246", /* … */ ++ [TREE_VERTICAL] = "\342\224\202 ", /* │ */ ++ [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ ++ [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ ++ [TREE_SPACE] = " ", /* */ ++ [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ ++ [BLACK_CIRCLE] = "\342\227\217", /* ● */ ++ [BULLET] = "\342\200\242", /* • */ ++ [ARROW] = "\342\206\222", /* → */ ++ [MDASH] = "\342\200\223", /* – */ ++ [ELLIPSIS] = "\342\200\246", /* … */ ++ [MU] = "\316\274", /* μ */ ++ [CHECK_MARK] = "\342\234\223", /* ✓ */ ++ [CROSS_MARK] = "\342\234\227", /* ✗ */ ++ [ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 */ ++ [HAPPY_SMILEY] = "\360\237\230\200", /* 😀 */ ++ [SLIGHTLY_HAPPY_SMILEY] = "\360\237\231\202", /* 🙂 */ ++ [NEUTRAL_SMILEY] = "\360\237\230\220", /* 😐 */ ++ [SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201", /* 🙁 */ ++ [UNHAPPY_SMILEY] = "\360\237\230\250", /* 😨️️ */ ++ [DEPRESSED_SMILEY] = "\360\237\244\242", /* 🤢 */ + }, + }; + +- return draw_table[is_locale_utf8()][code]; ++ assert(code < _SPECIAL_GLYPH_MAX); ++ ++ return draw_table[code >= _SPECIAL_GLYPH_FIRST_SMILEY ? emoji_enabled() : is_locale_utf8()][code]; + } + + static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { +diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h +index 775fe8bc72..368675f286 100644 +--- a/src/basic/locale-util.h ++++ b/src/basic/locale-util.h +@@ -45,9 +45,21 @@ typedef enum { + TREE_SPACE, + TRIANGULAR_BULLET, + BLACK_CIRCLE, ++ BULLET, + ARROW, + MDASH, + ELLIPSIS, ++ MU, ++ CHECK_MARK, ++ CROSS_MARK, ++ _SPECIAL_GLYPH_FIRST_SMILEY, ++ ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_SMILEY, ++ HAPPY_SMILEY, ++ SLIGHTLY_HAPPY_SMILEY, ++ NEUTRAL_SMILEY, ++ SLIGHTLY_UNHAPPY_SMILEY, ++ UNHAPPY_SMILEY, ++ DEPRESSED_SMILEY, + _SPECIAL_GLYPH_MAX + } SpecialGlyph; + +diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c +index 8ffae8ca03..0c3f6a62ed 100644 +--- a/src/test/test-locale-util.c ++++ b/src/test/test-locale-util.c +@@ -65,7 +65,7 @@ static void test_keymaps(void) { + + #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x)) + static void dump_special_glyphs(void) { +- assert_cc(ELLIPSIS + 1 == _SPECIAL_GLYPH_MAX); ++ assert_cc(DEPRESSED_SMILEY + 1 == _SPECIAL_GLYPH_MAX); + + log_info("/* %s */", __func__); + +@@ -80,6 +80,16 @@ static void dump_special_glyphs(void) { + dump_glyph(ARROW); + dump_glyph(MDASH); + dump_glyph(ELLIPSIS); ++ dump_glyph(MU); ++ dump_glyph(CHECK_MARK); ++ dump_glyph(CROSS_MARK); ++ dump_glyph(ECSTATIC_SMILEY); ++ dump_glyph(HAPPY_SMILEY); ++ dump_glyph(SLIGHTLY_HAPPY_SMILEY); ++ dump_glyph(NEUTRAL_SMILEY); ++ dump_glyph(SLIGHTLY_UNHAPPY_SMILEY); ++ dump_glyph(UNHAPPY_SMILEY); ++ dump_glyph(DEPRESSED_SMILEY); + } + + int main(int argc, char *argv[]) { diff --git a/SOURCES/0139-analyze-add-new-security-verb.patch b/SOURCES/0139-analyze-add-new-security-verb.patch new file mode 100644 index 0000000..d8ea17b --- /dev/null +++ b/SOURCES/0139-analyze-add-new-security-verb.patch @@ -0,0 +1,2310 @@ +From f5bd75fb574b9be80879fda5a889ddfd2b706248 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 09:32:17 +0100 +Subject: [PATCH] analyze: add new security verb + +(cherry picked from commit ec16f3b6dd8b03e3ce6eff1fa9f21432208ef42b) + +Conflicts: + src/analyze/analyze.c + +Resolves: #1689832 +--- + src/analyze/analyze-security.c | 2078 ++++++++++++++++++++++++++++++++ + src/analyze/analyze-security.h | 12 + + src/analyze/analyze.c | 16 + + src/analyze/meson.build | 2 + + src/basic/format-table.c | 1 - + src/basic/macro.h | 13 +- + src/basic/terminal-util.c | 53 +- + src/basic/terminal-util.h | 1 + + 8 files changed, 2150 insertions(+), 26 deletions(-) + create mode 100644 src/analyze/analyze-security.c + create mode 100644 src/analyze/analyze-security.h + +diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c +new file mode 100644 +index 0000000000..541fc0d97a +--- /dev/null ++++ b/src/analyze/analyze-security.c +@@ -0,0 +1,2078 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++#include ++ ++#include "analyze-security.h" ++#include "bus-error.h" ++#include "bus-unit-util.h" ++#include "bus-util.h" ++#include "env-util.h" ++#include "format-table.h" ++#include "in-addr-util.h" ++#include "locale-util.h" ++#include "macro.h" ++#include "parse-util.h" ++#include "path-util.h" ++#include "seccomp-util.h" ++#include "set.h" ++#include "stdio-util.h" ++#include "strv.h" ++#include "terminal-util.h" ++#include "unit-def.h" ++#include "unit-name.h" ++ ++struct security_info { ++ char *id; ++ char *type; ++ char *load_state; ++ char *fragment_path; ++ bool default_dependencies; ++ ++ uint64_t ambient_capabilities; ++ uint64_t capability_bounding_set; ++ ++ char *user; ++ char **supplementary_groups; ++ bool dynamic_user; ++ ++ bool ip_address_deny_all; ++ bool ip_address_allow_localhost; ++ bool ip_address_allow_other; ++ ++ char *keyring_mode; ++ bool lock_personality; ++ bool memory_deny_write_execute; ++ bool no_new_privileges; ++ char *notify_access; ++ ++ bool private_devices; ++ bool private_mounts; ++ bool private_network; ++ bool private_tmp; ++ bool private_users; ++ ++ bool protect_control_groups; ++ bool protect_kernel_modules; ++ bool protect_kernel_tunables; ++ ++ char *protect_home; ++ char *protect_system; ++ ++ bool remove_ipc; ++ ++ bool restrict_address_family_inet; ++ bool restrict_address_family_unix; ++ bool restrict_address_family_netlink; ++ bool restrict_address_family_packet; ++ bool restrict_address_family_other; ++ ++ uint64_t restrict_namespaces; ++ bool restrict_realtime; ++ ++ char *root_directory; ++ char *root_image; ++ ++ bool delegate; ++ char *device_policy; ++ bool device_allow_non_empty; ++ ++ char **system_call_architectures; ++ ++ bool system_call_filter_whitelist; ++ Set *system_call_filter; ++ ++ uint32_t _umask; ++}; ++ ++struct security_assessor { ++ const char *id; ++ const char *description_good; ++ const char *description_bad; ++ const char *description_na; ++ const char *url; ++ uint64_t weight; ++ uint64_t range; ++ int (*assess)(const struct security_assessor *a, const struct security_info *info, const void *data, uint64_t *ret_badness, char **ret_description); ++ size_t offset; ++ uint64_t parameter; ++ bool default_dependencies_only; ++}; ++ ++static void security_info_free(struct security_info *i) { ++ if (!i) ++ return; ++ ++ free(i->id); ++ free(i->type); ++ free(i->load_state); ++ free(i->fragment_path); ++ ++ free(i->user); ++ ++ free(i->protect_home); ++ free(i->protect_system); ++ ++ free(i->root_directory); ++ free(i->root_image); ++ ++ free(i->keyring_mode); ++ free(i->notify_access); ++ ++ free(i->device_policy); ++ ++ strv_free(i->supplementary_groups); ++ strv_free(i->system_call_architectures); ++ ++ set_free_free(i->system_call_filter); ++} ++ ++static bool security_info_runs_privileged(const struct security_info *i) { ++ assert(i); ++ ++ if (STRPTR_IN_SET(i->user, "0", "root")) ++ return true; ++ ++ if (i->dynamic_user) ++ return false; ++ ++ return isempty(i->user); ++} ++ ++static int assess_bool( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ const bool *b = data; ++ ++ assert(b); ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = a->parameter ? *b : !*b; ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static int assess_user( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ _cleanup_free_ char *d = NULL; ++ uint64_t b; ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (streq_ptr(info->user, NOBODY_USER_NAME)) { ++ d = strdup("Service runs under as '" NOBODY_USER_NAME "' user, which should not be used for services"); ++ b = 9; ++ } else if (info->dynamic_user && !STR_IN_SET(info->user, "0", "root")) { ++ d = strdup("Service runs under a transient non-root user identity"); ++ b = 0; ++ } else if (info->user && !STR_IN_SET(info->user, "0", "root", "")) { ++ d = strdup("Service runs under a static non-root user identity"); ++ b = 0; ++ } else { ++ *ret_badness = 10; ++ *ret_description = NULL; ++ return 0; ++ } ++ ++ if (!d) ++ return log_oom(); ++ ++ *ret_badness = b; ++ *ret_description = TAKE_PTR(d); ++ ++ return 0; ++} ++ ++static int assess_protect_home( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ const char *description; ++ uint64_t badness; ++ char *copy; ++ int r; ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ badness = 10; ++ description = "Service has full access to home directories"; ++ ++ r = parse_boolean(info->protect_home); ++ if (r < 0) { ++ if (streq_ptr(info->protect_home, "read-only")) { ++ badness = 5; ++ description = "Service has read-only access to home directories"; ++ } else if (streq_ptr(info->protect_home, "tmpfs")) { ++ badness = 1; ++ description = "Service has access to fake empty home directories"; ++ } ++ } else if (r > 0) { ++ badness = 0; ++ description = "Service has no access to home directories"; ++ } ++ ++ copy = strdup(description); ++ if (!copy) ++ return log_oom(); ++ ++ *ret_badness = badness; ++ *ret_description = copy; ++ ++ return 0; ++} ++ ++static int assess_protect_system( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ const char *description; ++ uint64_t badness; ++ char *copy; ++ int r; ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ badness = 10; ++ description = "Service has full access the OS file hierarchy"; ++ ++ r = parse_boolean(info->protect_system); ++ if (r < 0) { ++ if (streq_ptr(info->protect_system, "full")) { ++ badness = 3; ++ description = "Service has very limited write access to OS file hierarchy"; ++ } else if (streq_ptr(info->protect_system, "strict")) { ++ badness = 0; ++ description = "Service has strict read-only access to the OS file hierarchy"; ++ } ++ } else if (r > 0) { ++ badness = 5; ++ description = "Service has limited write access to the OS file hierarchy"; ++ } ++ ++ copy = strdup(description); ++ if (!copy) ++ return log_oom(); ++ ++ *ret_badness = badness; ++ *ret_description = copy; ++ ++ return 0; ++} ++ ++static int assess_root_directory( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = ++ (isempty(info->root_directory) || ++ path_equal(info->root_directory, "/")) && ++ (isempty(info->root_image) || ++ path_equal(info->root_image, "/")); ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static int assess_capability_bounding_set( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = !!(info->capability_bounding_set & a->parameter); ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static int assess_umask( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ char *copy = NULL; ++ const char *d; ++ uint64_t b; ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (!FLAGS_SET(info->_umask, 0002)) { ++ d = "Files created by service are world-writable by default"; ++ b = 10; ++ } else if (!FLAGS_SET(info->_umask, 0004)) { ++ d = "Files created by service are world-readable by default"; ++ b = 5; ++ } else if (!FLAGS_SET(info->_umask, 0020)) { ++ d = "Files created by service are group-writable by default"; ++ b = 2; ++ } else if (!FLAGS_SET(info->_umask, 0040)) { ++ d = "Files created by service are group-readable by default"; ++ b = 1; ++ } else { ++ d = "Files created by service are accessible only by service's own user by default"; ++ b = 0; ++ } ++ ++ copy = strdup(d); ++ if (!copy) ++ return log_oom(); ++ ++ *ret_badness = b; ++ *ret_description = copy; ++ ++ return 0; ++} ++ ++static int assess_keyring_mode( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = !streq_ptr(info->keyring_mode, "private"); ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static int assess_notify_access( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = streq_ptr(info->notify_access, "all"); ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static int assess_remove_ipc( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (security_info_runs_privileged(info)) ++ *ret_badness = UINT64_MAX; ++ else ++ *ret_badness = !info->remove_ipc; ++ ++ *ret_description = NULL; ++ return 0; ++} ++ ++static int assess_supplementary_groups( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (security_info_runs_privileged(info)) ++ *ret_badness = UINT64_MAX; ++ else ++ *ret_badness = !strv_isempty(info->supplementary_groups); ++ ++ *ret_description = NULL; ++ return 0; ++} ++ ++static int assess_restrict_namespaces( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = !!(info->restrict_namespaces & a->parameter); ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static int assess_system_call_architectures( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ char *d; ++ uint64_t b; ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (strv_isempty(info->system_call_architectures)) { ++ b = 10; ++ d = strdup("Service may execute system calls with all ABIs"); ++ } else if (strv_equal(info->system_call_architectures, STRV_MAKE("native"))) { ++ b = 0; ++ d = strdup("Service may execute system calls only with native ABI"); ++ } else { ++ b = 8; ++ d = strdup("Service may execute system calls with multiple ABIs"); ++ } ++ ++ if (!d) ++ return log_oom(); ++ ++ *ret_badness = b; ++ *ret_description = d; ++ ++ return 0; ++} ++ ++static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterSet *f) { ++ const char *syscall; ++ ++ NULSTR_FOREACH(syscall, f->value) { ++ bool b; ++ ++ if (syscall[0] == '@') { ++ const SyscallFilterSet *g; ++ assert_se(g = syscall_filter_set_find(syscall)); ++ b = syscall_names_in_filter(s, whitelist, g); ++ } else { ++#if HAVE_SECCOMP ++ int id; ++ ++ /* Let's see if the system call actually exists on this platform, before complaining */ ++ id = seccomp_syscall_resolve_name(syscall); ++ if (id < 0) ++ continue; ++#endif ++ ++ b = set_contains(s, syscall); ++ } ++ ++ if (whitelist == b) { ++ log_debug("Offending syscall filter item: %s", syscall); ++ return true; /* bad! */ ++ } ++ } ++ ++ return false; ++} ++ ++static int assess_system_call_filter( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ const SyscallFilterSet *f; ++ char *d = NULL; ++ uint64_t b; ++ ++ assert(a); ++ assert(info); ++ assert(ret_badness); ++ assert(ret_description); ++ ++ assert(a->parameter < _SYSCALL_FILTER_SET_MAX); ++ f = syscall_filter_sets + a->parameter; ++ ++ if (!info->system_call_filter_whitelist && set_isempty(info->system_call_filter)) { ++ d = strdup("Service does not filter system calls"); ++ b = 10; ++ } else { ++ bool bad; ++ ++ log_debug("Analyzing system call filter, checking against: %s", f->name); ++ bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_whitelist, f); ++ log_debug("Result: %s", bad ? "bad" : "good"); ++ ++ if (info->system_call_filter_whitelist) { ++ if (bad) { ++ (void) asprintf(&d, "System call whitelist defined for service, and %s is included", f->name); ++ b = 9; ++ } else { ++ (void) asprintf(&d, "System call whitelist defined for service, and %s is not included", f->name); ++ b = 0; ++ } ++ } else { ++ if (bad) { ++ (void) asprintf(&d, "System call blacklist defined for service, and %s is not included", f->name); ++ b = 10; ++ } else { ++ (void) asprintf(&d, "System call blacklist defined for service, and %s is included", f->name); ++ b = 5; ++ } ++ } ++ } ++ ++ if (!d) ++ return log_oom(); ++ ++ *ret_badness = b; ++ *ret_description = d; ++ ++ return 0; ++} ++ ++static int assess_ip_address_allow( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ char *d = NULL; ++ uint64_t b; ++ ++ assert(info); ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (!info->ip_address_deny_all) { ++ d = strdup("Service does not define an IP address whitelist"); ++ b = 10; ++ } else if (info->ip_address_allow_other) { ++ d = strdup("Service defines IP address whitelist with non-localhost entries"); ++ b = 5; ++ } else if (info->ip_address_allow_localhost) { ++ d = strdup("Service defines IP address whitelits with only localhost entries"); ++ b = 2; ++ } else { ++ d = strdup("Service blocks all IP address ranges"); ++ b = 0; ++ } ++ ++ if (!d) ++ return log_oom(); ++ ++ *ret_badness = b; ++ *ret_description = d; ++ ++ return 0; ++} ++ ++static int assess_device_allow( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ char *d = NULL; ++ uint64_t b; ++ ++ assert(info); ++ assert(ret_badness); ++ assert(ret_description); ++ ++ if (STRPTR_IN_SET(info->device_policy, "strict", "closed")) { ++ ++ if (info->device_allow_non_empty) { ++ d = strdup("Service has a device ACL with some special devices"); ++ b = 5; ++ } else { ++ d = strdup("Service has a minimal device ACL"); ++ b = 0; ++ } ++ } else { ++ d = strdup("Service has no device ACL"); ++ b = 10; ++ } ++ ++ if (!d) ++ return log_oom(); ++ ++ *ret_badness = b; ++ *ret_description = d; ++ ++ return 0; ++} ++ ++static int assess_ambient_capabilities( ++ const struct security_assessor *a, ++ const struct security_info *info, ++ const void *data, ++ uint64_t *ret_badness, ++ char **ret_description) { ++ ++ assert(ret_badness); ++ assert(ret_description); ++ ++ *ret_badness = info->ambient_capabilities != 0; ++ *ret_description = NULL; ++ ++ return 0; ++} ++ ++static const struct security_assessor security_assessor_table[] = { ++ { ++ .id = "User=/DynamicUser=", ++ .description_bad = "Service runs as root user", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=", ++ .weight = 2000, ++ .range = 10, ++ .assess = assess_user, ++ }, ++ { ++ .id = "SupplementaryGroups=", ++ .description_good = "Service has no supplementary groups", ++ .description_bad = "Service runs with supplementary groups", ++ .description_na = "Service runs as root, option does not matter", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SupplementaryGroups=", ++ .weight = 200, ++ .range = 1, ++ .assess = assess_supplementary_groups, ++ }, ++ { ++ .id = "PrivateDevices=", ++ .description_good = "Service has no access to hardware devices", ++ .description_bad = "Service potentially has access to hardware devices", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, private_devices), ++ }, ++ { ++ .id = "PrivateMounts=", ++ .description_good = "Service cannot install system mounts", ++ .description_bad = "Service may install system mounts", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, private_mounts), ++ }, ++ { ++ .id = "PrivateNetwork=", ++ .description_good = "Service has no access to the host's network", ++ .description_bad = "Service has access to the host's network", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=", ++ .weight = 2500, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, private_network), ++ }, ++ { ++ .id = "PrivateTmp=", ++ .description_good = "Service has no access to other software's temporary files", ++ .description_bad = "Service has access to other software's temporary files", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, private_tmp), ++ .default_dependencies_only = true, ++ }, ++ { ++ .id = "PrivateUsers=", ++ .description_good = "Service does not have access to other users", ++ .description_bad = "Service has access to other users", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, private_users), ++ }, ++ { ++ .id = "ProtectControlGroups=", ++ .description_good = "Service cannot modify the control group file system", ++ .description_bad = "Service may modify to the control group file system", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, protect_control_groups), ++ }, ++ { ++ .id = "ProtectKernelModules=", ++ .description_good = "Service cannot load or read kernel modules", ++ .description_bad = "Service may load or read kernel modules", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, protect_kernel_modules), ++ }, ++ { ++ .id = "ProtectKernelTunables=", ++ .description_good = "Service cannot alter kernel tunables (/proc/sys, …)", ++ .description_bad = "Service may alter kernel tunables", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, protect_kernel_tunables), ++ }, ++ { ++ .id = "ProtectHome=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_protect_home, ++ .default_dependencies_only = true, ++ }, ++ { ++ .id = "ProtectSystem=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_protect_system, ++ .default_dependencies_only = true, ++ }, ++ { ++ .id = "RootDirectory=/RootImage=", ++ .description_good = "Service has its own root directory/image", ++ .description_bad = "Service runs within the host's root directory", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=", ++ .weight = 200, ++ .range = 1, ++ .assess = assess_root_directory, ++ .default_dependencies_only = true, ++ }, ++ { ++ .id = "LockPersonality=", ++ .description_good = "Service cannot change ABI personality", ++ .description_bad = "Service may change ABI personality", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, lock_personality), ++ }, ++ { ++ .id = "MemoryDenyWriteExecute=", ++ .description_good = "Service cannot create writable executable memory mappings", ++ .description_bad = "Service may create writable executable memory mappings", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, memory_deny_write_execute), ++ }, ++ { ++ .id = "NoNewPrivileges=", ++ .description_good = "Service processes cannot acquire new privileges", ++ .description_bad = "Service processes may acquire new privileges", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, no_new_privileges), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN", ++ .description_good = "Service has no administrator privileges", ++ .description_bad = "Service has administrator privileges", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = UINT64_C(1) << CAP_SYS_ADMIN, ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)", ++ .description_good = "Service cannot change UID/GID identities/capabilities", ++ .description_bad = "Service may change UID/GID identities/capabilities", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SETUID)| ++ (UINT64_C(1) << CAP_SETGID)| ++ (UINT64_C(1) << CAP_SETPCAP), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_PTRACE", ++ .description_good = "Service has no ptrace() debugging abilities", ++ .description_bad = "Service has ptrace() debugging abilities", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_PTRACE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_TIME", ++ .description_good = "Service processes cannot change the system clock", ++ .description_bad = "Service processes may change the system clock", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = UINT64_C(1) << CAP_SYS_TIME, ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_NET_ADMIN", ++ .description_good = "Service has no network configuration privileges", ++ .description_bad = "Service has network configuration privileges", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_NET_ADMIN), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_RAWIO", ++ .description_good = "Service has no raw I/O access", ++ .description_bad = "Service has raw I/O access", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_RAWIO), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_MODULE", ++ .description_good = "Service cannot load kernel modules", ++ .description_bad = "Service may load kernel modules", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_MODULE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_AUDIT_*", ++ .description_good = "Service has no audit subsystem access", ++ .description_bad = "Service has audit subsystem access", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_AUDIT_CONTROL) | ++ (UINT64_C(1) << CAP_AUDIT_READ) | ++ (UINT64_C(1) << CAP_AUDIT_WRITE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYSLOG", ++ .description_good = "Service has no access to kernel logging", ++ .description_bad = "Service has access to kernel logging", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYSLOG), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)", ++ .description_good = "Service has no privileges to change resource use parameters", ++ .description_bad = "Service has privileges to change resource use parameters", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_NICE) | ++ (UINT64_C(1) << CAP_SYS_RESOURCE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_MKNOD", ++ .description_good = "Service cannot create device nodes", ++ .description_bad = "Service may create device nodes", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_MKNOD), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)", ++ .description_good = "Service cannot change file ownership/access mode/capabilities", ++ .description_bad = "Service may change file ownership/access mode/capabilities unrestricted", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_CHOWN) | ++ (UINT64_C(1) << CAP_FSETID) | ++ (UINT64_C(1) << CAP_SETFCAP), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)", ++ .description_good = "Service cannot override UNIX file/IPC permission checks", ++ .description_bad = "Service may override UNIX file/IPC permission checks", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_DAC_OVERRIDE) | ++ (UINT64_C(1) << CAP_DAC_READ_SEARCH) | ++ (UINT64_C(1) << CAP_FOWNER) | ++ (UINT64_C(1) << CAP_IPC_OWNER), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_KILL", ++ .description_good = "Service cannot send UNIX signals to arbitrary processes", ++ .description_bad = "Service may send UNIX signals to arbitrary processes", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_KILL), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)", ++ .description_good = "Service has no elevated networking privileges", ++ .description_bad = "Service has elevated networking privileges", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_NET_BIND_SERVICE) | ++ (UINT64_C(1) << CAP_NET_BROADCAST) | ++ (UINT64_C(1) << CAP_NET_RAW), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_BOOT", ++ .description_good = "Service cannot issue reboot()", ++ .description_bad = "Service may issue reboot()", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_BOOT), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_MAC_*", ++ .description_good = "Service cannot adjust SMACK MAC", ++ .description_bad = "Service may adjust SMACK MAC", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_MAC_ADMIN)| ++ (UINT64_C(1) << CAP_MAC_OVERRIDE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE", ++ .description_good = "Service cannot mark files immutable", ++ .description_bad = "Service may mark files immutable", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 75, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_LINUX_IMMUTABLE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_IPC_LOCK", ++ .description_good = "Service cannot lock memory into RAM", ++ .description_bad = "Service may lock memory into RAM", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 50, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_IPC_LOCK), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_CHROOT", ++ .description_good = "Service cannot issue chroot()", ++ .description_bad = "Service may issue chroot()", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 50, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_CHROOT), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND", ++ .description_good = "Service cannot establish wake locks", ++ .description_bad = "Service may establish wake locks", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 25, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_BLOCK_SUSPEND), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_WAKE_ALARM", ++ .description_good = "Service cannot program timers that wake up the system", ++ .description_bad = "Service may program timers that wake up the system", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 25, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_WAKE_ALARM), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_LEASE", ++ .description_good = "Service cannot create file leases", ++ .description_bad = "Service may create file leases", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 25, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_LEASE), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG", ++ .description_good = "Service cannot issue vhangup()", ++ .description_bad = "Service may issue vhangup()", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 25, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_TTY_CONFIG), ++ }, ++ { ++ .id = "CapabilityBoundingSet=~CAP_SYS_PACCT", ++ .description_good = "Service cannot use acct()", ++ .description_bad = "Service may use acct()", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=", ++ .weight = 25, ++ .range = 1, ++ .assess = assess_capability_bounding_set, ++ .parameter = (UINT64_C(1) << CAP_SYS_PACCT), ++ }, ++ { ++ .id = "UMask=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=", ++ .weight = 100, ++ .range = 10, ++ .assess = assess_umask, ++ }, ++ { ++ .id = "KeyringMode=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=", ++ .description_good = "Service doesn't share key material with other services", ++ .description_bad = "Service shares key material with other service", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_keyring_mode, ++ }, ++ { ++ .id = "NotifyAccess=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=", ++ .description_good = "Service child processes cannot alter service state", ++ .description_bad = "Service child processes may alter service state", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_notify_access, ++ }, ++ { ++ .id = "RemoveIPC=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=", ++ .description_good = "Service user cannot leave SysV IPC objects around", ++ .description_bad = "Service user may leave SysV IPC objects around", ++ .description_na = "Service runs as root, option does not apply", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_remove_ipc, ++ .offset = offsetof(struct security_info, remove_ipc), ++ }, ++ { ++ .id = "Delegate=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=", ++ .description_good = "Service does not maintain its own delegated control group subtree", ++ .description_bad = "Service maintains its own delegated control group subtree", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, delegate), ++ .parameter = true, /* invert! */ ++ }, ++ { ++ .id = "RestrictRealtime=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=", ++ .description_good = "Service realtime scheduling access is restricted", ++ .description_bad = "Service may acquire realtime scheduling", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, restrict_realtime), ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWUSER", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create user namespaces", ++ .description_bad = "Service may create user namespaces", ++ .weight = 1500, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWUSER, ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWNS", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create file system namespaces", ++ .description_bad = "Service may create file system namespaces", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWNS, ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWIPC", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create IPC namespaces", ++ .description_bad = "Service may create IPC namespaces", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWIPC, ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWPID", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create process namespaces", ++ .description_bad = "Service may create process namespaces", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWPID, ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWCGROUP", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create cgroup namespaces", ++ .description_bad = "Service may create cgroup namespaces", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWCGROUP, ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWNET", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create network namespaces", ++ .description_bad = "Service may create network namespaces", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWNET, ++ }, ++ { ++ .id = "RestrictNamespaces=~CLONE_NEWUTS", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", ++ .description_good = "Service cannot create hostname namespaces", ++ .description_bad = "Service may create hostname namespaces", ++ .weight = 100, ++ .range = 1, ++ .assess = assess_restrict_namespaces, ++ .parameter = CLONE_NEWUTS, ++ }, ++ { ++ .id = "RestrictAddressFamilies=~AF_(INET|INET6)", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=", ++ .description_good = "Service cannot allocate Internet sockets", ++ .description_bad = "Service may allocate Internet sockets", ++ .weight = 1500, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, restrict_address_family_inet), ++ }, ++ { ++ .id = "RestrictAddressFamilies=~AF_UNIX", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=", ++ .description_good = "Service cannot allocate local sockets", ++ .description_bad = "Service may allocate local sockets", ++ .weight = 25, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, restrict_address_family_unix), ++ }, ++ { ++ .id = "RestrictAddressFamilies=~AF_NETLINK", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=", ++ .description_good = "Service cannot allocate netlink sockets", ++ .description_bad = "Service may allocate netlink sockets", ++ .weight = 200, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, restrict_address_family_netlink), ++ }, ++ { ++ .id = "RestrictAddressFamilies=~AF_PACKET", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=", ++ .description_good = "Service cannot allocate packet sockets", ++ .description_bad = "Service may allocate packet sockets", ++ .weight = 1000, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, restrict_address_family_packet), ++ }, ++ { ++ .id = "RestrictAddressFamilies=~…", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=", ++ .description_good = "Service cannot allocate exotic sockets", ++ .description_bad = "Service may allocate exotic sockets", ++ .weight = 1250, ++ .range = 1, ++ .assess = assess_bool, ++ .offset = offsetof(struct security_info, restrict_address_family_other), ++ }, ++ { ++ .id = "SystemCallArchitectures=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_architectures, ++ }, ++ { ++ .id = "SystemCallFilter=~@swap", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_SWAP, ++ }, ++ { ++ .id = "SystemCallFilter=~@obsolete", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 250, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_OBSOLETE, ++ }, ++ { ++ .id = "SystemCallFilter=~@clock", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_CLOCK, ++ }, ++ { ++ .id = "SystemCallFilter=~@cpu-emulation", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 250, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_CPU_EMULATION, ++ }, ++ { ++ .id = "SystemCallFilter=~@debug", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_DEBUG, ++ }, ++ { ++ .id = "SystemCallFilter=~@mount", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_MOUNT, ++ }, ++ { ++ .id = "SystemCallFilter=~@module", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_MODULE, ++ }, ++ { ++ .id = "SystemCallFilter=~@raw-io", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_RAW_IO, ++ }, ++ { ++ .id = "SystemCallFilter=~@reboot", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_REBOOT, ++ }, ++ { ++ .id = "SystemCallFilter=~@privileged", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 700, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_PRIVILEGED, ++ }, ++ { ++ .id = "SystemCallFilter=~@resources", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=", ++ .weight = 700, ++ .range = 10, ++ .assess = assess_system_call_filter, ++ .parameter = SYSCALL_FILTER_SET_RESOURCES, ++ }, ++ { ++ .id = "IPAddressDeny=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_ip_address_allow, ++ }, ++ { ++ .id = "DeviceAllow=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=", ++ .weight = 1000, ++ .range = 10, ++ .assess = assess_device_allow, ++ }, ++ { ++ .id = "AmbientCapabilities=", ++ .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=", ++ .description_good = "Service process does not receive ambient capabilities", ++ .description_bad = "Service process receives ambient capabilities", ++ .weight = 500, ++ .range = 1, ++ .assess = assess_ambient_capabilities, ++ }, ++}; ++ ++static int assess(const struct security_info *info, Table *overview_table, AnalyzeSecurityFlags flags) { ++ static const struct { ++ uint64_t exposure; ++ const char *name; ++ const char *color; ++ SpecialGlyph smiley; ++ } badness_table[] = { ++ { 100, "DANGEROUS", ANSI_HIGHLIGHT_RED, DEPRESSED_SMILEY }, ++ { 90, "UNSAFE", ANSI_HIGHLIGHT_RED, UNHAPPY_SMILEY }, ++ { 75, "EXPOSED", ANSI_HIGHLIGHT_YELLOW, SLIGHTLY_UNHAPPY_SMILEY }, ++ { 50, "MEDIUM", NULL, NEUTRAL_SMILEY }, ++ { 10, "OK", ANSI_HIGHLIGHT_GREEN, SLIGHTLY_HAPPY_SMILEY }, ++ { 1, "SAFE", ANSI_HIGHLIGHT_GREEN, HAPPY_SMILEY }, ++ { 0, "PERFECT", ANSI_HIGHLIGHT_GREEN, ECSTATIC_SMILEY }, ++ }; ++ ++ uint64_t badness_sum = 0, weight_sum = 0, exposure; ++ _cleanup_(table_unrefp) Table *details_table = NULL; ++ size_t i; ++ int r; ++ ++ if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) { ++ details_table = table_new("", "NAME", "DESCRIPTION", "WEIGHT", "BADNESS", "RANGE", "EXPOSURE"); ++ if (!details_table) ++ return log_oom(); ++ ++ (void) table_set_sort(details_table, 3, 1, (size_t) -1); ++ (void) table_set_reverse(details_table, 3, true); ++ ++ if (getenv_bool("SYSTEMD_ANALYZE_DEBUG") <= 0) ++ (void) table_set_display(details_table, 0, 1, 2, 6, (size_t) -1); ++ } ++ ++ for (i = 0; i < ELEMENTSOF(security_assessor_table); i++) { ++ const struct security_assessor *a = security_assessor_table + i; ++ _cleanup_free_ char *d = NULL; ++ uint64_t badness; ++ void *data; ++ ++ data = (uint8_t*) info + a->offset; ++ ++ if (a->default_dependencies_only && !info->default_dependencies) { ++ badness = UINT64_MAX; ++ d = strdup("Service runs in special boot phase, option does not apply"); ++ if (!d) ++ return log_oom(); ++ } else { ++ r = a->assess(a, info, data, &badness, &d); ++ if (r < 0) ++ return r; ++ } ++ ++ assert(a->range > 0); ++ ++ if (badness != UINT64_MAX) { ++ assert(badness <= a->range); ++ ++ badness_sum += DIV_ROUND_UP(badness * a->weight, a->range); ++ weight_sum += a->weight; ++ } ++ ++ if (details_table) { ++ const char *checkmark, *description, *color = NULL; ++ TableCell *cell; ++ ++ if (badness == UINT64_MAX) { ++ checkmark = " "; ++ description = a->description_na; ++ color = NULL; ++ } else if (badness == a->range) { ++ checkmark = special_glyph(CROSS_MARK); ++ description = a->description_bad; ++ color = ansi_highlight_red(); ++ } else if (badness == 0) { ++ checkmark = special_glyph(CHECK_MARK); ++ description = a->description_good; ++ color = ansi_highlight_green(); ++ } else { ++ checkmark = special_glyph(CROSS_MARK); ++ description = NULL; ++ color = ansi_highlight_red(); ++ } ++ ++ if (d) ++ description = d; ++ ++ r = table_add_cell_full(details_table, &cell, TABLE_STRING, checkmark, 1, 1, 0, 0, 0); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ if (color) ++ (void) table_set_color(details_table, cell, color); ++ ++ r = table_add_cell(details_table, &cell, TABLE_STRING, a->id); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ if (a->url) ++ (void) table_set_url(details_table, cell, a->url); ++ ++ r = table_add_cell(details_table, NULL, TABLE_STRING, description); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ ++ r = table_add_cell(details_table, &cell, TABLE_UINT64, &a->weight); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ (void) table_set_align_percent(details_table, cell, 100); ++ ++ r = table_add_cell(details_table, &cell, TABLE_UINT64, &badness); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ (void) table_set_align_percent(details_table, cell, 100); ++ ++ r = table_add_cell(details_table, &cell, TABLE_UINT64, &a->range); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ (void) table_set_align_percent(details_table, cell, 100); ++ ++ r = table_add_cell(details_table, &cell, TABLE_EMPTY, NULL); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ (void) table_set_align_percent(details_table, cell, 100); ++ } ++ } ++ ++ if (details_table) { ++ size_t row; ++ ++ for (row = 1; row < table_get_rows(details_table); row++) { ++ char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1]; ++ const uint64_t *weight, *badness, *range; ++ TableCell *cell; ++ uint64_t x; ++ ++ assert_se(weight = table_get_at(details_table, row, 3)); ++ assert_se(badness = table_get_at(details_table, row, 4)); ++ assert_se(range = table_get_at(details_table, row, 5)); ++ ++ if (*badness == UINT64_MAX || *badness == 0) ++ continue; ++ ++ assert_se(cell = table_get_cell(details_table, row, 6)); ++ ++ x = DIV_ROUND_UP(DIV_ROUND_UP(*badness * *weight * 100U, *range), weight_sum); ++ xsprintf(buf, "%" PRIu64 ".%" PRIu64, x / 10, x % 10); ++ ++ r = table_update(details_table, cell, TABLE_STRING, buf); ++ if (r < 0) ++ return log_error_errno(r, "Failed to update cell in table: %m"); ++ } ++ ++ r = table_print(details_table, stdout); ++ if (r < 0) ++ return log_error_errno(r, "Failed to output table: %m"); ++ } ++ ++ exposure = DIV_ROUND_UP(badness_sum * 100U, weight_sum); ++ ++ for (i = 0; i < ELEMENTSOF(badness_table); i++) ++ if (exposure >= badness_table[i].exposure) ++ break; ++ ++ assert(i < ELEMENTSOF(badness_table)); ++ ++ if (details_table) { ++ _cleanup_free_ char *clickable = NULL; ++ const char *name; ++ ++ /* If we shall output the details table, also print the brief summary underneath */ ++ ++ if (info->fragment_path) { ++ r = terminal_urlify_path(info->fragment_path, info->id, &clickable); ++ if (r < 0) ++ return log_oom(); ++ ++ name = clickable; ++ } else ++ name = info->id; ++ ++ printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n", ++ special_glyph(ARROW), ++ ansi_highlight(), ++ name, ++ ansi_normal(), ++ colors_enabled() ? strempty(badness_table[i].color) : "", ++ exposure / 10, exposure % 10, ++ badness_table[i].name, ++ ansi_normal(), ++ special_glyph(badness_table[i].smiley)); ++ } ++ ++ fflush(stdout); ++ ++ if (overview_table) { ++ char buf[DECIMAL_STR_MAX(uint64_t) + 1 + DECIMAL_STR_MAX(uint64_t) + 1]; ++ TableCell *cell; ++ ++ r = table_add_cell(overview_table, &cell, TABLE_STRING, info->id); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ if (info->fragment_path) { ++ _cleanup_free_ char *url = NULL; ++ ++ r = file_url_from_path(info->fragment_path, &url); ++ if (r < 0) ++ return log_error_errno(r, "Failed to generate URL from path: %m"); ++ ++ (void) table_set_url(overview_table, cell, url); ++ } ++ ++ xsprintf(buf, "%" PRIu64 ".%" PRIu64, exposure / 10, exposure % 10); ++ r = table_add_cell(overview_table, &cell, TABLE_STRING, buf); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ (void) table_set_align_percent(overview_table, cell, 100); ++ ++ r = table_add_cell(overview_table, &cell, TABLE_STRING, badness_table[i].name); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ (void) table_set_color(overview_table, cell, strempty(badness_table[i].color)); ++ ++ r = table_add_cell(overview_table, NULL, TABLE_STRING, special_glyph(badness_table[i].smiley)); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add cell to table: %m"); ++ } ++ ++ return 0; ++} ++ ++static int property_read_restrict_address_families( ++ sd_bus *bus, ++ const char *member, ++ sd_bus_message *m, ++ sd_bus_error *error, ++ void *userdata) { ++ ++ struct security_info *info = userdata; ++ int whitelist, r; ++ ++ assert(bus); ++ assert(member); ++ assert(m); ++ ++ r = sd_bus_message_enter_container(m, 'r', "bas"); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_read(m, "b", &whitelist); ++ if (r < 0) ++ return r; ++ ++ info->restrict_address_family_inet = ++ info->restrict_address_family_unix = ++ info->restrict_address_family_netlink = ++ info->restrict_address_family_packet = ++ info->restrict_address_family_other = whitelist; ++ ++ r = sd_bus_message_enter_container(m, 'a', "s"); ++ if (r < 0) ++ return r; ++ ++ for (;;) { ++ const char *name; ++ ++ r = sd_bus_message_read(m, "s", &name); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ if (STR_IN_SET(name, "AF_INET", "AF_INET6")) ++ info->restrict_address_family_inet = !whitelist; ++ else if (streq(name, "AF_UNIX")) ++ info->restrict_address_family_unix = !whitelist; ++ else if (streq(name, "AF_NETLINK")) ++ info->restrict_address_family_netlink = !whitelist; ++ else if (streq(name, "AF_PACKET")) ++ info->restrict_address_family_packet = !whitelist; ++ else ++ info->restrict_address_family_other = !whitelist; ++ } ++ ++ r = sd_bus_message_exit_container(m); ++ if (r < 0) ++ return r; ++ ++ return sd_bus_message_exit_container(m); ++} ++ ++static int property_read_system_call_filter( ++ sd_bus *bus, ++ const char *member, ++ sd_bus_message *m, ++ sd_bus_error *error, ++ void *userdata) { ++ ++ struct security_info *info = userdata; ++ int whitelist, r; ++ ++ assert(bus); ++ assert(member); ++ assert(m); ++ ++ r = sd_bus_message_enter_container(m, 'r', "bas"); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_read(m, "b", &whitelist); ++ if (r < 0) ++ return r; ++ ++ info->system_call_filter_whitelist = whitelist; ++ ++ r = sd_bus_message_enter_container(m, 'a', "s"); ++ if (r < 0) ++ return r; ++ ++ for (;;) { ++ const char *name; ++ ++ r = sd_bus_message_read(m, "s", &name); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ r = set_ensure_allocated(&info->system_call_filter, &string_hash_ops); ++ if (r < 0) ++ return r; ++ ++ r = set_put_strdup(info->system_call_filter, name); ++ if (r < 0) ++ return r; ++ } ++ ++ r = sd_bus_message_exit_container(m); ++ if (r < 0) ++ return r; ++ ++ return sd_bus_message_exit_container(m); ++} ++ ++static int property_read_ip_address_allow( ++ sd_bus *bus, ++ const char *member, ++ sd_bus_message *m, ++ sd_bus_error *error, ++ void *userdata) { ++ ++ struct security_info *info = userdata; ++ bool deny_ipv4 = false, deny_ipv6 = false; ++ int r; ++ ++ assert(bus); ++ assert(member); ++ assert(m); ++ ++ r = sd_bus_message_enter_container(m, 'a', "(iayu)"); ++ if (r < 0) ++ return r; ++ ++ for (;;) { ++ const void *data; ++ size_t size; ++ int32_t family; ++ uint32_t prefixlen; ++ ++ r = sd_bus_message_enter_container(m, 'r', "iayu"); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ r = sd_bus_message_read(m, "i", &family); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_read_array(m, 'y', &data, &size); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_read(m, "u", &prefixlen); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_exit_container(m); ++ if (r < 0) ++ return r; ++ ++ if (streq(member, "IPAddressAllow")) { ++ union in_addr_union u; ++ ++ if (family == AF_INET && size == 4 && prefixlen == 8) ++ memcpy(&u.in, data, size); ++ else if (family == AF_INET6 && size == 16 && prefixlen == 128) ++ memcpy(&u.in6, data, size); ++ else { ++ info->ip_address_allow_other = true; ++ continue; ++ } ++ ++ if (in_addr_is_localhost(family, &u)) ++ info->ip_address_allow_localhost = true; ++ else ++ info->ip_address_allow_other = true; ++ } else { ++ assert(streq(member, "IPAddressDeny")); ++ ++ if (family == AF_INET && size == 4 && prefixlen == 0) ++ deny_ipv4 = true; ++ else if (family == AF_INET6 && size == 16 && prefixlen == 0) ++ deny_ipv6 = true; ++ } ++ } ++ ++ info->ip_address_deny_all = deny_ipv4 && deny_ipv6; ++ ++ return sd_bus_message_exit_container(m); ++} ++ ++static int property_read_device_allow( ++ sd_bus *bus, ++ const char *member, ++ sd_bus_message *m, ++ sd_bus_error *error, ++ void *userdata) { ++ ++ struct security_info *info = userdata; ++ size_t n = 0; ++ int r; ++ ++ assert(bus); ++ assert(member); ++ assert(m); ++ ++ r = sd_bus_message_enter_container(m, 'a', "(ss)"); ++ if (r < 0) ++ return r; ++ ++ for (;;) { ++ const char *name, *policy; ++ ++ r = sd_bus_message_read(m, "(ss)", &name, &policy); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ n++; ++ } ++ ++ info->device_allow_non_empty = n > 0; ++ ++ return sd_bus_message_exit_container(m); ++} ++ ++static int acquire_security_info(sd_bus *bus, const char *name, struct security_info *info, AnalyzeSecurityFlags flags) { ++ ++ static const struct bus_properties_map security_map[] = { ++ { "AmbientCapabilities", "t", NULL, offsetof(struct security_info, ambient_capabilities) }, ++ { "CapabilityBoundingSet", "t", NULL, offsetof(struct security_info, capability_bounding_set) }, ++ { "DefaultDependencies", "b", NULL, offsetof(struct security_info, default_dependencies) }, ++ { "Delegate", "b", NULL, offsetof(struct security_info, delegate) }, ++ { "DeviceAllow", "a(ss)", property_read_device_allow, 0 }, ++ { "DevicePolicy", "s", NULL, offsetof(struct security_info, device_policy) }, ++ { "DynamicUser", "b", NULL, offsetof(struct security_info, dynamic_user) }, ++ { "FragmentPath", "s", NULL, offsetof(struct security_info, fragment_path) }, ++ { "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 }, ++ { "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 }, ++ { "Id", "s", NULL, offsetof(struct security_info, id) }, ++ { "KeyringMode", "s", NULL, offsetof(struct security_info, keyring_mode) }, ++ { "LoadState", "s", NULL, offsetof(struct security_info, load_state) }, ++ { "LockPersonality", "b", NULL, offsetof(struct security_info, lock_personality) }, ++ { "MemoryDenyWriteExecute", "b", NULL, offsetof(struct security_info, memory_deny_write_execute) }, ++ { "NoNewPrivileges", "b", NULL, offsetof(struct security_info, no_new_privileges) }, ++ { "NotifyAccess", "s", NULL, offsetof(struct security_info, notify_access) }, ++ { "PrivateDevices", "b", NULL, offsetof(struct security_info, private_devices) }, ++ { "PrivateMounts", "b", NULL, offsetof(struct security_info, private_mounts) }, ++ { "PrivateNetwork", "b", NULL, offsetof(struct security_info, private_network) }, ++ { "PrivateTmp", "b", NULL, offsetof(struct security_info, private_tmp) }, ++ { "PrivateUsers", "b", NULL, offsetof(struct security_info, private_users) }, ++ { "PrivateUsers", "b", NULL, offsetof(struct security_info, private_users) }, ++ { "ProtectControlGroups", "b", NULL, offsetof(struct security_info, protect_control_groups) }, ++ { "ProtectHome", "s", NULL, offsetof(struct security_info, protect_home) }, ++ { "ProtectKernelModules", "b", NULL, offsetof(struct security_info, protect_kernel_modules) }, ++ { "ProtectKernelTunables", "b", NULL, offsetof(struct security_info, protect_kernel_tunables) }, ++ { "ProtectSystem", "s", NULL, offsetof(struct security_info, protect_system) }, ++ { "RemoveIPC", "b", NULL, offsetof(struct security_info, remove_ipc) }, ++ { "RestrictAddressFamilies", "(bas)", property_read_restrict_address_families, 0 }, ++ { "RestrictNamespaces", "t", NULL, offsetof(struct security_info, restrict_namespaces) }, ++ { "RestrictRealtime", "b", NULL, offsetof(struct security_info, restrict_realtime) }, ++ { "RootDirectory", "s", NULL, offsetof(struct security_info, root_directory) }, ++ { "RootImage", "s", NULL, offsetof(struct security_info, root_image) }, ++ { "SupplementaryGroups", "as", NULL, offsetof(struct security_info, supplementary_groups) }, ++ { "SystemCallArchitectures", "as", NULL, offsetof(struct security_info, system_call_architectures) }, ++ { "SystemCallFilter", "(as)", property_read_system_call_filter, 0 }, ++ { "Type", "s", NULL, offsetof(struct security_info, type) }, ++ { "UMask", "u", NULL, offsetof(struct security_info, _umask) }, ++ { "User", "s", NULL, offsetof(struct security_info, user) }, ++ {} ++ }; ++ ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _cleanup_free_ char *path = NULL; ++ int r; ++ ++ /* Note: this mangles *info on failure! */ ++ ++ assert(bus); ++ assert(name); ++ assert(info); ++ ++ path = unit_dbus_path_from_name(name); ++ if (!path) ++ return log_oom(); ++ ++ r = bus_map_all_properties(bus, ++ "org.freedesktop.systemd1", ++ path, ++ security_map, ++ BUS_MAP_STRDUP|BUS_MAP_BOOLEAN_AS_BOOL, ++ &error, ++ NULL, ++ info); ++ if (r < 0) ++ return log_error_errno(r, "Failed to get unit properties: %s", bus_error_message(&error, r)); ++ ++ if (!streq_ptr(info->load_state, "loaded")) { ++ ++ if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LOADED)) ++ return -EMEDIUMTYPE; ++ ++ if (streq_ptr(info->load_state, "not-found")) ++ log_error("Unit %s not found, cannot analyze.", name); ++ else if (streq_ptr(info->load_state, "masked")) ++ log_error("Unit %s is masked, cannot analyze.", name); ++ else ++ log_error("Unit %s not loaded properly, cannot analyze.", name); ++ ++ return -EINVAL; ++ } ++ ++ if (FLAGS_SET(flags, ANALYZE_SECURITY_ONLY_LONG_RUNNING) && streq_ptr(info->type, "oneshot")) ++ return -EMEDIUMTYPE; ++ ++ if (info->private_devices || ++ info->private_tmp || ++ info->protect_control_groups || ++ info->protect_kernel_tunables || ++ info->protect_kernel_modules || ++ !streq_ptr(info->protect_home, "no") || ++ !streq_ptr(info->protect_system, "no") || ++ info->root_image) ++ info->private_mounts = true; ++ ++ if (info->protect_kernel_modules) ++ info->capability_bounding_set &= ~(UINT64_C(1) << CAP_SYS_MODULE); ++ ++ if (info->private_devices) ++ info->capability_bounding_set &= ~((UINT64_C(1) << CAP_MKNOD) | ++ (UINT64_C(1) << CAP_SYS_RAWIO)); ++ ++ return 0; ++} ++ ++static int analyze_security_one(sd_bus *bus, const char *name, Table* overview_table, AnalyzeSecurityFlags flags) { ++ _cleanup_(security_info_free) struct security_info info = { ++ .default_dependencies = true, ++ .capability_bounding_set = UINT64_MAX, ++ .restrict_namespaces = UINT64_MAX, ++ ._umask = 0002, ++ }; ++ int r; ++ ++ assert(bus); ++ assert(name); ++ ++ r = acquire_security_info(bus, name, &info, flags); ++ if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */ ++ return 0; ++ if (r < 0) ++ return r; ++ ++ r = assess(&info, overview_table, flags); ++ if (r < 0) ++ return r; ++ ++ return 0; ++} ++ ++int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) { ++ _cleanup_(table_unrefp) Table *overview_table = NULL; ++ int ret = 0, r; ++ ++ assert(bus); ++ ++ if (strv_length(units) != 1) { ++ overview_table = table_new("UNIT", "EXPOSURE", "PREDICATE", "HAPPY"); ++ if (!overview_table) ++ return log_oom(); ++ } ++ ++ if (strv_isempty(units)) { ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; ++ _cleanup_strv_free_ char **list = NULL; ++ size_t allocated = 0, n = 0; ++ char **i; ++ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "ListUnits", ++ &error, &reply, ++ NULL); ++ if (r < 0) ++ return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); ++ ++ r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ for (;;) { ++ UnitInfo info; ++ char *copy = NULL; ++ ++ r = bus_parse_unit_info(reply, &info); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ if (r == 0) ++ break; ++ ++ if (!endswith(info.id, ".service")) ++ continue; ++ ++ if (!GREEDY_REALLOC(list, allocated, n+2)) ++ return log_oom(); ++ ++ copy = strdup(info.id); ++ if (!copy) ++ return log_oom(); ++ ++ list[n++] = copy; ++ list[n] = NULL; ++ } ++ ++ strv_sort(list); ++ ++ flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING; ++ ++ STRV_FOREACH(i, list) { ++ r = analyze_security_one(bus, *i, overview_table, flags); ++ if (r < 0 && ret >= 0) ++ ret = r; ++ } ++ ++ } else { ++ char **i; ++ ++ STRV_FOREACH(i, units) { ++ _cleanup_free_ char *mangled = NULL, *instance = NULL; ++ const char *name; ++ ++ if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT) && i != units) { ++ putc('\n', stdout); ++ fflush(stdout); ++ } ++ ++ r = unit_name_mangle_with_suffix(*i, 0, ".service", &mangled); ++ if (r < 0) ++ return log_error_errno(r, "Failed to mangle unit name '%s': %m", *i); ++ ++ if (!endswith(mangled, ".service")) { ++ log_error("Unit %s is not a service unit, refusing.", *i); ++ return -EINVAL; ++ } ++ ++ if (unit_name_is_valid(mangled, UNIT_NAME_TEMPLATE)) { ++ r = unit_name_replace_instance(mangled, "test-instance", &instance); ++ if (r < 0) ++ return log_oom(); ++ ++ name = instance; ++ } else ++ name = mangled; ++ ++ r = analyze_security_one(bus, name, overview_table, flags); ++ if (r < 0 && ret >= 0) ++ ret = r; ++ } ++ } ++ ++ if (overview_table) { ++ if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) { ++ putc('\n', stdout); ++ fflush(stdout); ++ } ++ ++ r = table_print(overview_table, stdout); ++ if (r < 0) ++ return log_error_errno(r, "Failed to output table: %m"); ++ } ++ ++ return ret; ++} +diff --git a/src/analyze/analyze-security.h b/src/analyze/analyze-security.h +new file mode 100644 +index 0000000000..c00ae7c80a +--- /dev/null ++++ b/src/analyze/analyze-security.h +@@ -0,0 +1,12 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++#pragma once ++ ++#include "sd-bus.h" ++ ++typedef enum AnalyzeSecurityFlags { ++ ANALYZE_SECURITY_SHORT = 1 << 0, ++ ANALYZE_SECURITY_ONLY_LOADED = 1 << 1, ++ ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2, ++} AnalyzeSecurityFlags; ++ ++int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags); +diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c +index dc7d2ab0f6..c30a133fc0 100644 +--- a/src/analyze/analyze.c ++++ b/src/analyze/analyze.c +@@ -11,6 +11,7 @@ + #include "sd-bus.h" + + #include "alloc-util.h" ++#include "analyze-security.h" + #include "analyze-verify.h" + #include "bus-error.h" + #include "bus-unit-util.h" +@@ -1659,6 +1660,19 @@ static int do_verify(int argc, char *argv[], void *userdata) { + return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators); + } + ++static int do_security(int argc, char *argv[], void *userdata) { ++ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; ++ int r; ++ ++ r = acquire_bus(&bus, NULL); ++ if (r < 0) ++ return log_error_errno(r, "Failed to create bus connection: %m"); ++ ++ (void) pager_open(arg_no_pager, false); ++ ++ return analyze_security(bus, strv_skip(argv, 1), 0); ++} ++ + static int help(int argc, char *argv[], void *userdata) { + + (void) pager_open(arg_no_pager, false); +@@ -1696,6 +1710,7 @@ static int help(int argc, char *argv[], void *userdata) { + " verify FILE... Check unit files for correctness\n" + " calendar SPEC... Validate repetitive calendar time events\n" + " service-watchdogs [BOOL] Get/set service watchdog state\n" ++ " security [UNIT...] Analyze security of unit\n" + , program_invocation_short_name); + + /* When updating this list, including descriptions, apply +@@ -1884,6 +1899,7 @@ int main(int argc, char *argv[]) { + { "verify", 2, VERB_ANY, 0, do_verify }, + { "calendar", 2, VERB_ANY, 0, test_calendar }, + { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs }, ++ { "security", VERB_ANY, VERB_ANY, 0, do_security }, + {} + }; + +diff --git a/src/analyze/meson.build b/src/analyze/meson.build +index 3a69a259b1..4db4dfa552 100644 +--- a/src/analyze/meson.build ++++ b/src/analyze/meson.build +@@ -4,4 +4,6 @@ systemd_analyze_sources = files(''' + analyze.c + analyze-verify.c + analyze-verify.h ++ analyze-security.c ++ analyze-security.h + '''.split()) +diff --git a/src/basic/format-table.c b/src/basic/format-table.c +index 844b92f41c..c541e92b3c 100644 +--- a/src/basic/format-table.c ++++ b/src/basic/format-table.c +@@ -10,7 +10,6 @@ + #include "gunicode.h" + #include "pager.h" + #include "parse-util.h" +-#include "pretty-print.h" + #include "string-util.h" + #include "terminal-util.h" + #include "time-util.h" +diff --git a/src/basic/macro.h b/src/basic/macro.h +index 79ab02b27a..0fe6a62aa8 100644 +--- a/src/basic/macro.h ++++ b/src/basic/macro.h +@@ -249,12 +249,13 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { + * computation should be possible in the given type. Therefore, we use + * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the + * quotient and the remainder, so both should be equally fast. */ +-#define DIV_ROUND_UP(_x, _y) \ +- __extension__ ({ \ +- const typeof(_x) __x = (_x); \ +- const typeof(_y) __y = (_y); \ +- (__x / __y + !!(__x % __y)); \ +- }) ++#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) ++#define __DIV_ROUND_UP(xq, x, yq, y) \ ++ ({ \ ++ const typeof(x) UNIQ_T(X, xq) = (x); \ ++ const typeof(y) UNIQ_T(Y, yq) = (y); \ ++ (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \ ++ }) + + #define assert_message_se(expr, message) \ + do { \ +diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c +index f4af0e6522..e2bbe8187d 100644 +--- a/src/basic/terminal-util.c ++++ b/src/basic/terminal-util.c +@@ -1320,10 +1320,38 @@ int terminal_urlify(const char *url, const char *text, char **ret) { + return 0; + } + +-int terminal_urlify_path(const char *path, const char *text, char **ret) { ++int file_url_from_path(const char *path, char **ret) { + _cleanup_free_ char *absolute = NULL; + struct utsname u; +- const char *url; ++ char *url = NULL; ++ int r; ++ ++ if (uname(&u) < 0) ++ return -errno; ++ ++ if (!path_is_absolute(path)) { ++ r = path_make_absolute_cwd(path, &absolute); ++ if (r < 0) ++ return r; ++ ++ path = absolute; ++ } ++ ++ /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local ++ * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested ++ * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly ++ * careful with validating the strings either. */ ++ ++ url = strjoin("file://", u.nodename, path); ++ if (!url) ++ return -ENOMEM; ++ ++ *ret = url; ++ return 0; ++} ++ ++int terminal_urlify_path(const char *path, const char *text, char **ret) { ++ _cleanup_free_ char *url = NULL; + int r; + + assert(path); +@@ -1348,27 +1376,14 @@ int terminal_urlify_path(const char *path, const char *text, char **ret) { + return 0; + } + +- if (uname(&u) < 0) +- return -errno; +- +- if (!path_is_absolute(path)) { +- r = path_make_absolute_cwd(path, &absolute); +- if (r < 0) +- return r; +- +- path = absolute; +- } +- +- /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local +- * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested +- * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly +- * careful with validating the strings either. */ +- +- url = strjoina("file://", u.nodename, path); ++ r = file_url_from_path(path, &url); ++ if (r < 0) ++ return r; + + return terminal_urlify(url, text, ret); + } + ++ + static int cat_file(const char *filename, bool newline) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *urlified = NULL; +diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h +index 0055b72343..3f23ecfd3d 100644 +--- a/src/basic/terminal-util.h ++++ b/src/basic/terminal-util.h +@@ -154,6 +154,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode); + int vt_default_utf8(void); + int vt_reset_keyboard(int fd); + ++int file_url_from_path(const char *path, char **ret); + int terminal_urlify(const char *url, const char *text, char **ret); + int terminal_urlify_path(const char *path, const char *text, char **ret); + diff --git a/SOURCES/0140-tests-add-a-rudimentary-fuzzer-for-server_process_sy.patch b/SOURCES/0140-tests-add-a-rudimentary-fuzzer-for-server_process_sy.patch new file mode 100644 index 0000000..83c8577 --- /dev/null +++ b/SOURCES/0140-tests-add-a-rudimentary-fuzzer-for-server_process_sy.patch @@ -0,0 +1,66 @@ +From 86cf4118a04afbc67d0231f8b53cf3629258032a Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 3 Sep 2018 06:18:26 +0300 +Subject: [PATCH] tests: add a rudimentary fuzzer for + server_process_syslog_message (#9979) + +(cherry picked from commit a70f343cacf03ac51cdefb0d2e7651b04fd2e23a) + +Resolves: #1696224 +--- + src/fuzz/fuzz-journald-syslog.c | 29 +++++++++++++++++++++++++++++ + src/fuzz/meson.build | 5 +++++ + 2 files changed, 34 insertions(+) + create mode 100644 src/fuzz/fuzz-journald-syslog.c + +diff --git a/src/fuzz/fuzz-journald-syslog.c b/src/fuzz/fuzz-journald-syslog.c +new file mode 100644 +index 0000000000..7730f60875 +--- /dev/null ++++ b/src/fuzz/fuzz-journald-syslog.c +@@ -0,0 +1,29 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "alloc-util.h" ++#include "fuzz.h" ++#include "journald-server.h" ++#include "journald-syslog.h" ++#include "sd-event.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ Server s = {}; ++ char *label = NULL; ++ size_t label_len = 0; ++ struct ucred *ucred = NULL; ++ struct timeval *tv = NULL; ++ ++ if (size == 0) ++ return 0; ++ ++ assert_se(sd_event_default(&s.event) >= 0); ++ s.syslog_fd = s.native_fd = s.stdout_fd = s.dev_kmsg_fd = s.audit_fd = s.hostname_fd = s.notify_fd = -1; ++ s.buffer = memdup_suffix0(data, size); ++ assert_se(s.buffer); ++ s.buffer_size = size + 1; ++ s.storage = STORAGE_NONE; ++ server_process_syslog_message(&s, s.buffer, size, ucred, tv, label, label_len); ++ server_done(&s); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index ad44778889..28770b68b8 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -19,6 +19,11 @@ fuzzers += [ + libshared], + [libmount]], + ++ [['src/fuzz/fuzz-journald-syslog.c'], ++ [libjournal_core, ++ libshared], ++ [libselinux]], ++ + [['src/fuzz/fuzz-journal-remote.c'], + [libsystemd_journal_remote, + libshared], diff --git a/SOURCES/0141-journald-make-it-clear-that-dev_kmsg_record-modifies.patch b/SOURCES/0141-journald-make-it-clear-that-dev_kmsg_record-modifies.patch new file mode 100644 index 0000000..6078dd4 --- /dev/null +++ b/SOURCES/0141-journald-make-it-clear-that-dev_kmsg_record-modifies.patch @@ -0,0 +1,30 @@ +From 7e902f1e31bfdd6542343c1656dae3b6853228a6 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 10 Aug 2018 12:45:42 +0000 +Subject: [PATCH] journald: make it clear that dev_kmsg_record modifies the + string passed to it + +The function replaces a couple commas, a semicolon and the final newline with +zero bytes in the string passed to it. The 'const' seems to have been added +by accident during a bulk edit (more specifically 3b3154df7e2773332bb814). + +(cherry picked from commit 1e0c5fc2a76e4f3d508331f410899c50493e1fc9) + +Resolves: #1696224 +--- + src/journal/journald-kmsg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c +index e9aff13168..7ad673362a 100644 +--- a/src/journal/journald-kmsg.c ++++ b/src/journal/journald-kmsg.c +@@ -93,7 +93,7 @@ static bool is_us(const char *identifier, const char *pid) { + streq(identifier, program_invocation_short_name); + } + +-static void dev_kmsg_record(Server *s, const char *p, size_t l) { ++static void dev_kmsg_record(Server *s, char *p, size_t l) { + + _cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL; + struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; diff --git a/SOURCES/0142-journald-free-the-allocated-memory-before-returning-.patch b/SOURCES/0142-journald-free-the-allocated-memory-before-returning-.patch new file mode 100644 index 0000000..e1aab73 --- /dev/null +++ b/SOURCES/0142-journald-free-the-allocated-memory-before-returning-.patch @@ -0,0 +1,28 @@ +From d8600cb5319060f294049a81320f5de4c48cf5d5 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 10 Aug 2018 12:52:07 +0000 +Subject: [PATCH] journald: free the allocated memory before returning from + dev_kmsg_record + +This fixes a minor memory leak. + +(cherry picked from commit 30eddcd51b8a472e05d3b8d1f0b89fbd3e094d71) + +Resolves: #1696224 +--- + src/journal/journald-kmsg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c +index 7ad673362a..7644bebfc8 100644 +--- a/src/journal/journald-kmsg.c ++++ b/src/journal/journald-kmsg.c +@@ -191,7 +191,7 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) { + + e = memchr(k, '\n', l); + if (!e) +- return; ++ goto finish; + + *e = 0; + diff --git a/SOURCES/0143-tests-rework-the-code-fuzzing-journald.patch b/SOURCES/0143-tests-rework-the-code-fuzzing-journald.patch new file mode 100644 index 0000000..3c86a94 --- /dev/null +++ b/SOURCES/0143-tests-rework-the-code-fuzzing-journald.patch @@ -0,0 +1,89 @@ +From 452aefc33ab5ebe9c3725d7680ce16399b486a9b Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 3 Sep 2018 06:46:24 +0000 +Subject: [PATCH] tests: rework the code fuzzing journald + +This should make it easier to add a new fuzzer without a lot of +duplication. + +(cherry picked from commit b1bd453f36b9428b6bf9feba31fa0a2b36143e9c) + +Resolves: #1696224 +--- + src/fuzz/fuzz-journald-syslog.c | 23 ++--------------------- + src/fuzz/fuzz-journald.h | 30 ++++++++++++++++++++++++++++++ + 2 files changed, 32 insertions(+), 21 deletions(-) + create mode 100644 src/fuzz/fuzz-journald.h + +diff --git a/src/fuzz/fuzz-journald-syslog.c b/src/fuzz/fuzz-journald-syslog.c +index 7730f60875..100f0ce691 100644 +--- a/src/fuzz/fuzz-journald-syslog.c ++++ b/src/fuzz/fuzz-journald-syslog.c +@@ -1,29 +1,10 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + +-#include "alloc-util.h" + #include "fuzz.h" +-#include "journald-server.h" ++#include "fuzz-journald.h" + #include "journald-syslog.h" +-#include "sd-event.h" + + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { +- Server s = {}; +- char *label = NULL; +- size_t label_len = 0; +- struct ucred *ucred = NULL; +- struct timeval *tv = NULL; +- +- if (size == 0) +- return 0; +- +- assert_se(sd_event_default(&s.event) >= 0); +- s.syslog_fd = s.native_fd = s.stdout_fd = s.dev_kmsg_fd = s.audit_fd = s.hostname_fd = s.notify_fd = -1; +- s.buffer = memdup_suffix0(data, size); +- assert_se(s.buffer); +- s.buffer_size = size + 1; +- s.storage = STORAGE_NONE; +- server_process_syslog_message(&s, s.buffer, size, ucred, tv, label, label_len); +- server_done(&s); +- ++ fuzz_journald_processing_function(data, size, server_process_syslog_message); + return 0; + } +diff --git a/src/fuzz/fuzz-journald.h b/src/fuzz/fuzz-journald.h +new file mode 100644 +index 0000000000..e66ef54c9b +--- /dev/null ++++ b/src/fuzz/fuzz-journald.h +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++#pragma once ++ ++#include "alloc-util.h" ++#include "journald-server.h" ++#include "sd-event.h" ++ ++static void fuzz_journald_processing_function( ++ const uint8_t *data, ++ size_t size, ++ void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) ++ ) { ++ Server s = {}; ++ char *label = NULL; ++ size_t label_len = 0; ++ struct ucred *ucred = NULL; ++ struct timeval *tv = NULL; ++ ++ if (size == 0) ++ return; ++ ++ assert_se(sd_event_default(&s.event) >= 0); ++ s.syslog_fd = s.native_fd = s.stdout_fd = s.dev_kmsg_fd = s.audit_fd = s.hostname_fd = s.notify_fd = -1; ++ s.buffer = memdup_suffix0(data, size); ++ assert_se(s.buffer); ++ s.buffer_size = size + 1; ++ s.storage = STORAGE_NONE; ++ (*f)(&s, s.buffer, size, ucred, tv, label, label_len); ++ server_done(&s); ++} diff --git a/SOURCES/0144-journald-make-server_process_native_message-compatib.patch b/SOURCES/0144-journald-make-server_process_native_message-compatib.patch new file mode 100644 index 0000000..cd7d314 --- /dev/null +++ b/SOURCES/0144-journald-make-server_process_native_message-compatib.patch @@ -0,0 +1,40 @@ +From eb711891df269ea784e3c9adc3483d9ec86c25e9 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 3 Sep 2018 07:03:10 +0000 +Subject: [PATCH] journald: make server_process_native_message compatible with + fuzz_journald_processing_function + +(cherry picked from commit 21acb27b71f6284a57e4e9f3ac5f0d38721ef4eb) + +Resolves: #1696224 +--- + src/journal/journald-native.c | 2 +- + src/journal/journald-native.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c +index da62448ca6..466b6f7213 100644 +--- a/src/journal/journald-native.c ++++ b/src/journal/journald-native.c +@@ -293,7 +293,7 @@ finish: + + void server_process_native_message( + Server *s, +- const void *buffer, size_t buffer_size, ++ const char *buffer, size_t buffer_size, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, size_t label_len) { +diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h +index 7211d4fab4..2a33ef74c5 100644 +--- a/src/journal/journald-native.h ++++ b/src/journal/journald-native.h +@@ -5,7 +5,7 @@ + + void server_process_native_message( + Server *s, +- const void *buffer, ++ const char *buffer, + size_t buffer_size, + const struct ucred *ucred, + const struct timeval *tv, diff --git a/SOURCES/0145-tests-add-a-fuzzer-for-server_process_native_message.patch b/SOURCES/0145-tests-add-a-fuzzer-for-server_process_native_message.patch new file mode 100644 index 0000000..01faecd --- /dev/null +++ b/SOURCES/0145-tests-add-a-fuzzer-for-server_process_native_message.patch @@ -0,0 +1,46 @@ +From f9a28e4e070ed86a0c5138dbfb98a60f00beb8d4 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 3 Sep 2018 07:05:48 +0000 +Subject: [PATCH] tests: add a fuzzer for server_process_native_message + +(cherry picked from commit 9cdea02db57a36442ad9e9afcd67760ca319173a) + +Resolves: #1696224 +--- + src/fuzz/fuzz-journald-native.c | 10 ++++++++++ + src/fuzz/meson.build | 5 +++++ + 2 files changed, 15 insertions(+) + create mode 100644 src/fuzz/fuzz-journald-native.c + +diff --git a/src/fuzz/fuzz-journald-native.c b/src/fuzz/fuzz-journald-native.c +new file mode 100644 +index 0000000000..f4de5fd8eb +--- /dev/null ++++ b/src/fuzz/fuzz-journald-native.c +@@ -0,0 +1,10 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "fuzz.h" ++#include "fuzz-journald.h" ++#include "journald-native.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ fuzz_journald_processing_function(data, size, server_process_native_message); ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 28770b68b8..5a97ef5091 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -19,6 +19,11 @@ fuzzers += [ + libshared], + [libmount]], + ++ [['src/fuzz/fuzz-journald-native.c'], ++ [libjournal_core, ++ libshared], ++ [libselinux]], ++ + [['src/fuzz/fuzz-journald-syslog.c'], + [libjournal_core, + libshared], diff --git a/SOURCES/0146-tests-add-a-fuzzer-for-sd-ndisc.patch b/SOURCES/0146-tests-add-a-fuzzer-for-sd-ndisc.patch new file mode 100644 index 0000000..c1b2ccf --- /dev/null +++ b/SOURCES/0146-tests-add-a-fuzzer-for-sd-ndisc.patch @@ -0,0 +1,98 @@ +From aec984020cd22ac8a199bcd067047fba50850889 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Wed, 26 Sep 2018 15:04:26 +0000 +Subject: [PATCH] tests: add a fuzzer for sd-ndisc + +(cherry picked from commit 0f0a1dad7d69802a7e6c7fc9aba350f0e87c1952) + +Resolves: #1696224 +--- + src/fuzz/fuzz-ndisc-rs.c | 57 ++++++++++++++++++++++++++++++++++++++++ + src/fuzz/meson.build | 10 +++++++ + 2 files changed, 67 insertions(+) + create mode 100644 src/fuzz/fuzz-ndisc-rs.c + +diff --git a/src/fuzz/fuzz-ndisc-rs.c b/src/fuzz/fuzz-ndisc-rs.c +new file mode 100644 +index 0000000000..7f2d8f8649 +--- /dev/null ++++ b/src/fuzz/fuzz-ndisc-rs.c +@@ -0,0 +1,57 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++#include ++ ++#include "alloc-util.h" ++#include "icmp6-util.h" ++#include "fuzz.h" ++#include "sd-ndisc.h" ++#include "socket-util.h" ++#include "ndisc-internal.h" ++ ++static int test_fd[2]; ++ ++int icmp6_bind_router_solicitation(int index) { ++ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) >= 0); ++ return test_fd[0]; ++} ++ ++int icmp6_bind_router_advertisement(int index) { ++ return -ENOSYS; ++} ++ ++int icmp6_receive(int fd, void *iov_base, size_t iov_len, ++ struct in6_addr *dst, triple_timestamp *timestamp) { ++ assert_se(read(fd, iov_base, iov_len) == (ssize_t) iov_len); ++ ++ if (timestamp) ++ triple_timestamp_get(timestamp); ++ ++ return 0; ++} ++ ++int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { ++ return 0; ++} ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ struct ether_addr mac_addr = { ++ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} ++ }; ++ _cleanup_(sd_event_unrefp) sd_event *e = NULL; ++ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; ++ ++ assert_se(sd_event_new(&e) >= 0); ++ assert_se(sd_ndisc_new(&nd) >= 0); ++ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); ++ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); ++ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); ++ assert_se(sd_ndisc_start(nd) >= 0); ++ assert_se(write(test_fd[1], data, size) == (ssize_t) size); ++ (void) sd_event_run(e, (uint64_t) -1); ++ assert_se(sd_ndisc_stop(nd) >= 0); ++ close(test_fd[1]); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 5a97ef5091..5c81ac0c5b 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -14,6 +14,16 @@ fuzzers += [ + libshared], + []], + ++ [['src/fuzz/fuzz-ndisc-rs.c', ++ 'src/libsystemd-network/dhcp-identifier.h', ++ 'src/libsystemd-network/dhcp-identifier.c', ++ 'src/libsystemd-network/icmp6-util.h', ++ 'src/systemd/sd-dhcp6-client.h', ++ 'src/systemd/sd-ndisc.h'], ++ [libshared, ++ libsystemd_network], ++ []], ++ + [['src/fuzz/fuzz-unit-file.c'], + [libcore, + libshared], diff --git a/SOURCES/0147-ndisc-fix-two-infinite-loops.patch b/SOURCES/0147-ndisc-fix-two-infinite-loops.patch new file mode 100644 index 0000000..34ff112 --- /dev/null +++ b/SOURCES/0147-ndisc-fix-two-infinite-loops.patch @@ -0,0 +1,34 @@ +From 0318890ed2325b916ccfa5a59ede98e6a4b5fe60 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 28 Sep 2018 19:28:05 +0900 +Subject: [PATCH] ndisc: fix two infinite loops + +(cherry picked from commit f3241c61f12dbd8f0ed37419ae272e291d09461d) + +Resolves: #1696224 +--- + src/libsystemd-network/ndisc-router.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c +index 25b693a458..3806804435 100644 +--- a/src/libsystemd-network/ndisc-router.c ++++ b/src/libsystemd-network/ndisc-router.c +@@ -189,7 +189,7 @@ int ndisc_router_parse(sd_ndisc_router *rt) { + + if (has_mtu) { + log_ndisc("MTU option specified twice, ignoring."); +- continue; ++ break; + } + + if (length != 8) { +@@ -230,7 +230,7 @@ int ndisc_router_parse(sd_ndisc_router *rt) { + + if (has_flag_extension) { + log_ndisc("Flags extension option specified twice, ignoring."); +- continue; ++ break; + } + + if (length < 1*8) { diff --git a/SOURCES/0148-tests-add-reproducers-for-several-issues-uncovered-w.patch b/SOURCES/0148-tests-add-reproducers-for-several-issues-uncovered-w.patch new file mode 100644 index 0000000..723fbfa --- /dev/null +++ b/SOURCES/0148-tests-add-reproducers-for-several-issues-uncovered-w.patch @@ -0,0 +1,70 @@ +From 8918fcc041a7f58e55d6d2b40e8dc3c396819b67 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Sun, 2 Sep 2018 18:13:31 +0000 +Subject: [PATCH] tests: add reproducers for several issues uncovered with + fuzz-journald-syslog + +This is a follow-up to a70f343cacf03ac51cdefb0d2e. + +(cherry picked from commit 3311c74d0560e4aa6a223f5e288a5fbf2404d3fa) + +Resolves: #1696224 +--- + test/fuzz-regressions/fuzz-journald-syslog/github-9795 | 1 + + test/fuzz-regressions/fuzz-journald-syslog/github-9820 | 1 + + test/fuzz-regressions/fuzz-journald-syslog/github-9827 | 1 + + test/fuzz-regressions/fuzz-journald-syslog/github-9829 | 1 + + test/fuzz-regressions/meson.build | 4 ++++ + 5 files changed, 8 insertions(+) + create mode 100644 test/fuzz-regressions/fuzz-journald-syslog/github-9795 + create mode 100644 test/fuzz-regressions/fuzz-journald-syslog/github-9820 + create mode 100644 test/fuzz-regressions/fuzz-journald-syslog/github-9827 + create mode 100644 test/fuzz-regressions/fuzz-journald-syslog/github-9829 + +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9795 b/test/fuzz-regressions/fuzz-journald-syslog/github-9795 +new file mode 100644 +index 0000000000..0519ecba6e +--- /dev/null ++++ b/test/fuzz-regressions/fuzz-journald-syslog/github-9795 +@@ -0,0 +1 @@ ++ +\ No newline at end of file +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9820 b/test/fuzz-regressions/fuzz-journald-syslog/github-9820 +new file mode 100644 +index 0000000000..55e1bb5967 +--- /dev/null ++++ b/test/fuzz-regressions/fuzz-journald-syslog/github-9820 +@@ -0,0 +1 @@ ++<13>Aug 4 04:08:03 something-is-about-to-go-wrong: +\ No newline at end of file +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9827 b/test/fuzz-regressions/fuzz-journald-syslog/github-9827 +new file mode 100644 +index 0000000000..6787e487a4 +--- /dev/null ++++ b/test/fuzz-regressions/fuzz-journald-syslog/github-9827 +@@ -0,0 +1 @@ ++<> +\ No newline at end of file +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9829 b/test/fuzz-regressions/fuzz-journald-syslog/github-9829 +new file mode 100644 +index 0000000000..22ded55aa2 +--- /dev/null ++++ b/test/fuzz-regressions/fuzz-journald-syslog/github-9829 +@@ -0,0 +1 @@ ++: +\ No newline at end of file +diff --git a/test/fuzz-regressions/meson.build b/test/fuzz-regressions/meson.build +index 3a2c306492..74fd88cfcd 100644 +--- a/test/fuzz-regressions/meson.build ++++ b/test/fuzz-regressions/meson.build +@@ -18,6 +18,10 @@ fuzz_regression_tests = ''' + fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 + fuzz-journal-remote/oss-fuzz-8659 + fuzz-journal-remote/oss-fuzz-8686 ++ fuzz-journald-syslog/github-9795 ++ fuzz-journald-syslog/github-9820 ++ fuzz-journald-syslog/github-9827 ++ fuzz-journald-syslog/github-9829 + fuzz-unit-file/oss-fuzz-6884 + fuzz-unit-file/oss-fuzz-6885 + fuzz-unit-file/oss-fuzz-6886 diff --git a/SOURCES/0149-tests-add-a-reproducer-for-an-infinite-loop-in-ndisc.patch b/SOURCES/0149-tests-add-a-reproducer-for-an-infinite-loop-in-ndisc.patch new file mode 100644 index 0000000..438dee1 --- /dev/null +++ b/SOURCES/0149-tests-add-a-reproducer-for-an-infinite-loop-in-ndisc.patch @@ -0,0 +1,46 @@ +From 514d782be9af0bd3e37a4e224816d3ba70b51ec0 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Wed, 26 Sep 2018 15:10:21 +0000 +Subject: [PATCH] tests: add a reproducer for an infinite loop in + ndisc_handle_datagram + +=0 ndisc_router_parse (rt=0x60d000000110) at ../src/libsystemd-network/ndisc-router.c:126 +=1 0x000055555558dc67 in ndisc_handle_datagram (nd=0x608000000020, rt=0x60d000000110) at ../src/libsystemd-network/sd-ndisc.c:170 +=2 0x000055555558e65d in ndisc_recv (s=0x611000000040, fd=4, revents=1, userdata=0x608000000020) at ../src/libsystemd-network/sd-ndisc.c:233 +=3 0x00007ffff63913a8 in source_dispatch (s=0x611000000040) at ../src/libsystemd/sd-event/sd-event.c:3042 +=4 0x00007ffff6395eab in sd_event_dispatch (e=0x617000000080) at ../src/libsystemd/sd-event/sd-event.c:3455 +=5 0x00007ffff6396b12 in sd_event_run (e=0x617000000080, timeout=18446744073709551615) at ../src/libsystemd/sd-event/sd-event.c:3512 +=6 0x0000555555583f5c in LLVMFuzzerTestOneInput (data=0x6060000000e0 "\206", size=53) at ../src/fuzz/fuzz-ndisc-rs.c:422 +=7 0x0000555555586356 in main (argc=2, argv=0x7fffffffe3d8) at ../src/fuzz/fuzz-main.c:33 + +(cherry picked from commit df30e78e02f653c9e6ee6677b7ccaea21d3dcd7d) + +Resolves: #1696224 +--- + ...imeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 | Bin 0 -> 53 bytes + test/fuzz-regressions/meson.build | 1 + + 2 files changed, 1 insertion(+) + create mode 100644 test/fuzz-regressions/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 + +diff --git a/test/fuzz-regressions/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 b/test/fuzz-regressions/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 +new file mode 100644 +index 0000000000000000000000000000000000000000..410cf38c1ec2156680e80160825b883fb4f12aa9 +GIT binary patch +literal 53 +ucmZo;U|{$U0h55t23AHOm-#;v2(&S9GpRCgaeXR_WB`f-fhq$7NEHAcu@3A2 + +literal 0 +HcmV?d00001 + +diff --git a/test/fuzz-regressions/meson.build b/test/fuzz-regressions/meson.build +index 74fd88cfcd..7aa2cbce11 100644 +--- a/test/fuzz-regressions/meson.build ++++ b/test/fuzz-regressions/meson.build +@@ -22,6 +22,7 @@ fuzz_regression_tests = ''' + fuzz-journald-syslog/github-9820 + fuzz-journald-syslog/github-9827 + fuzz-journald-syslog/github-9829 ++ fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 + fuzz-unit-file/oss-fuzz-6884 + fuzz-unit-file/oss-fuzz-6885 + fuzz-unit-file/oss-fuzz-6886 diff --git a/SOURCES/0150-tests-add-a-reproducer-for-another-infinite-loop-in-.patch b/SOURCES/0150-tests-add-a-reproducer-for-another-infinite-loop-in-.patch new file mode 100644 index 0000000..8ad518f --- /dev/null +++ b/SOURCES/0150-tests-add-a-reproducer-for-another-infinite-loop-in-.patch @@ -0,0 +1,37 @@ +From 1d33acf3ae230456db55fb7d153047cbd6b76623 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Wed, 26 Sep 2018 18:09:09 +0000 +Subject: [PATCH] tests: add a reproducer for another infinite loop in + ndisc_handle_datagram + +(cherry picked from commit bbb393877b2cfcbe2f205c902ca7d9f7ce91f1a1) + +Resolves: #1696224 +--- + ...imeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b | Bin 0 -> 71 bytes + test/fuzz-regressions/meson.build | 1 + + 2 files changed, 1 insertion(+) + create mode 100644 test/fuzz-regressions/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b + +diff --git a/test/fuzz-regressions/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b b/test/fuzz-regressions/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b +new file mode 100644 +index 0000000000000000000000000000000000000000..04e871fbcbddfe0642bd6855228bf8da163ad6e3 +GIT binary patch +literal 71 +ucmZo;U}$4tu#$oUW@d)Jzy1TkUp6M@U)f7q +Date: Sat, 7 Jul 2018 17:43:40 +0200 +Subject: [PATCH] fuzz: rename "fuzz-corpus" directory to just "fuzz" + +Also, all corpus subdirectories are named exactly the same as the fuzzer they +are for. This makes the paths a bit longer, but easier. + +(cherry picked from commit 93b575b26605c347a717b2aa24ddf9cad08b8080) + +Resolves: #1696224 +--- + test/{fuzz-corpus => fuzz}/.gitattributes | 0 + .../fuzz-dhcp-server}/discover-existing | Bin + .../fuzz-dhcp-server}/discover-new | Bin + .../dhcp-server => fuzz/fuzz-dhcp-server}/release | Bin + .../fuzz-dhcp-server}/request-existing | Bin + .../fuzz-dhcp-server}/request-new | Bin + .../fuzz-dhcp-server}/request-reboot | Bin + .../fuzz-dhcp-server}/request-renew | Bin + .../fuzz-journal-remote}/invalid-ts.txt | Bin + .../fuzz-journal-remote}/sample.txt | 0 + .../dev-mapper-fedora_krowka\\x2dswap.swap" | 0 + .../fuzz-unit-file}/directives.service | 0 + .../unit-file => fuzz/fuzz-unit-file}/empty.scope | 0 + .../unit-file => fuzz/fuzz-unit-file}/machine.slice | 0 + .../proc-sys-fs-binfmt_misc.automount | 0 + .../unit-file => fuzz/fuzz-unit-file}/syslog.socket | 0 + .../systemd-ask-password-console.path | 0 + .../fuzz-unit-file}/systemd-machined.service | 0 + .../fuzz-unit-file}/systemd-resolved.service | 0 + .../fuzz-unit-file}/systemd-tmpfiles-clean.timer | 0 + .../unit-file => fuzz/fuzz-unit-file}/timers.target | 0 + .../fuzz-unit-file}/var-lib-machines.mount | 0 + tools/oss-fuzz.sh | 6 ++++-- + 23 files changed, 4 insertions(+), 2 deletions(-) + rename test/{fuzz-corpus => fuzz}/.gitattributes (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/discover-existing (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/discover-new (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/release (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/request-existing (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/request-new (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/request-reboot (100%) + rename test/{fuzz-corpus/dhcp-server => fuzz/fuzz-dhcp-server}/request-renew (100%) + rename test/{fuzz-corpus/journal-remote => fuzz/fuzz-journal-remote}/invalid-ts.txt (100%) + rename test/{fuzz-corpus/journal-remote => fuzz/fuzz-journal-remote}/sample.txt (100%) + rename "test/fuzz-corpus/unit-file/dev-mapper-fedora_krowka\\x2dswap.swap" => "test/fuzz/fuzz-unit-file/dev-mapper-fedora_krowka\\x2dswap.swap" (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/directives.service (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/empty.scope (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/machine.slice (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/proc-sys-fs-binfmt_misc.automount (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/syslog.socket (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/systemd-ask-password-console.path (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/systemd-machined.service (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/systemd-resolved.service (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/systemd-tmpfiles-clean.timer (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/timers.target (100%) + rename test/{fuzz-corpus/unit-file => fuzz/fuzz-unit-file}/var-lib-machines.mount (100%) + +diff --git a/test/fuzz-corpus/.gitattributes b/test/fuzz/.gitattributes +similarity index 100% +rename from test/fuzz-corpus/.gitattributes +rename to test/fuzz/.gitattributes +diff --git a/test/fuzz-corpus/dhcp-server/discover-existing b/test/fuzz/fuzz-dhcp-server/discover-existing +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/discover-existing +rename to test/fuzz/fuzz-dhcp-server/discover-existing +diff --git a/test/fuzz-corpus/dhcp-server/discover-new b/test/fuzz/fuzz-dhcp-server/discover-new +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/discover-new +rename to test/fuzz/fuzz-dhcp-server/discover-new +diff --git a/test/fuzz-corpus/dhcp-server/release b/test/fuzz/fuzz-dhcp-server/release +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/release +rename to test/fuzz/fuzz-dhcp-server/release +diff --git a/test/fuzz-corpus/dhcp-server/request-existing b/test/fuzz/fuzz-dhcp-server/request-existing +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/request-existing +rename to test/fuzz/fuzz-dhcp-server/request-existing +diff --git a/test/fuzz-corpus/dhcp-server/request-new b/test/fuzz/fuzz-dhcp-server/request-new +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/request-new +rename to test/fuzz/fuzz-dhcp-server/request-new +diff --git a/test/fuzz-corpus/dhcp-server/request-reboot b/test/fuzz/fuzz-dhcp-server/request-reboot +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/request-reboot +rename to test/fuzz/fuzz-dhcp-server/request-reboot +diff --git a/test/fuzz-corpus/dhcp-server/request-renew b/test/fuzz/fuzz-dhcp-server/request-renew +similarity index 100% +rename from test/fuzz-corpus/dhcp-server/request-renew +rename to test/fuzz/fuzz-dhcp-server/request-renew +diff --git a/test/fuzz-corpus/journal-remote/invalid-ts.txt b/test/fuzz/fuzz-journal-remote/invalid-ts.txt +similarity index 100% +rename from test/fuzz-corpus/journal-remote/invalid-ts.txt +rename to test/fuzz/fuzz-journal-remote/invalid-ts.txt +diff --git a/test/fuzz-corpus/journal-remote/sample.txt b/test/fuzz/fuzz-journal-remote/sample.txt +similarity index 100% +rename from test/fuzz-corpus/journal-remote/sample.txt +rename to test/fuzz/fuzz-journal-remote/sample.txt +diff --git "a/test/fuzz-corpus/unit-file/dev-mapper-fedora_krowka\\x2dswap.swap" "b/test/fuzz/fuzz-unit-file/dev-mapper-fedora_krowka\\x2dswap.swap" +similarity index 100% +rename from "test/fuzz-corpus/unit-file/dev-mapper-fedora_krowka\\x2dswap.swap" +rename to "test/fuzz/fuzz-unit-file/dev-mapper-fedora_krowka\\x2dswap.swap" +diff --git a/test/fuzz-corpus/unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +similarity index 100% +rename from test/fuzz-corpus/unit-file/directives.service +rename to test/fuzz/fuzz-unit-file/directives.service +diff --git a/test/fuzz-corpus/unit-file/empty.scope b/test/fuzz/fuzz-unit-file/empty.scope +similarity index 100% +rename from test/fuzz-corpus/unit-file/empty.scope +rename to test/fuzz/fuzz-unit-file/empty.scope +diff --git a/test/fuzz-corpus/unit-file/machine.slice b/test/fuzz/fuzz-unit-file/machine.slice +similarity index 100% +rename from test/fuzz-corpus/unit-file/machine.slice +rename to test/fuzz/fuzz-unit-file/machine.slice +diff --git a/test/fuzz-corpus/unit-file/proc-sys-fs-binfmt_misc.automount b/test/fuzz/fuzz-unit-file/proc-sys-fs-binfmt_misc.automount +similarity index 100% +rename from test/fuzz-corpus/unit-file/proc-sys-fs-binfmt_misc.automount +rename to test/fuzz/fuzz-unit-file/proc-sys-fs-binfmt_misc.automount +diff --git a/test/fuzz-corpus/unit-file/syslog.socket b/test/fuzz/fuzz-unit-file/syslog.socket +similarity index 100% +rename from test/fuzz-corpus/unit-file/syslog.socket +rename to test/fuzz/fuzz-unit-file/syslog.socket +diff --git a/test/fuzz-corpus/unit-file/systemd-ask-password-console.path b/test/fuzz/fuzz-unit-file/systemd-ask-password-console.path +similarity index 100% +rename from test/fuzz-corpus/unit-file/systemd-ask-password-console.path +rename to test/fuzz/fuzz-unit-file/systemd-ask-password-console.path +diff --git a/test/fuzz-corpus/unit-file/systemd-machined.service b/test/fuzz/fuzz-unit-file/systemd-machined.service +similarity index 100% +rename from test/fuzz-corpus/unit-file/systemd-machined.service +rename to test/fuzz/fuzz-unit-file/systemd-machined.service +diff --git a/test/fuzz-corpus/unit-file/systemd-resolved.service b/test/fuzz/fuzz-unit-file/systemd-resolved.service +similarity index 100% +rename from test/fuzz-corpus/unit-file/systemd-resolved.service +rename to test/fuzz/fuzz-unit-file/systemd-resolved.service +diff --git a/test/fuzz-corpus/unit-file/systemd-tmpfiles-clean.timer b/test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer +similarity index 100% +rename from test/fuzz-corpus/unit-file/systemd-tmpfiles-clean.timer +rename to test/fuzz/fuzz-unit-file/systemd-tmpfiles-clean.timer +diff --git a/test/fuzz-corpus/unit-file/timers.target b/test/fuzz/fuzz-unit-file/timers.target +similarity index 100% +rename from test/fuzz-corpus/unit-file/timers.target +rename to test/fuzz/fuzz-unit-file/timers.target +diff --git a/test/fuzz-corpus/unit-file/var-lib-machines.mount b/test/fuzz/fuzz-unit-file/var-lib-machines.mount +similarity index 100% +rename from test/fuzz-corpus/unit-file/var-lib-machines.mount +rename to test/fuzz/fuzz-unit-file/var-lib-machines.mount +diff --git a/tools/oss-fuzz.sh b/tools/oss-fuzz.sh +index 200407fcca..451cc665ce 100755 +--- a/tools/oss-fuzz.sh ++++ b/tools/oss-fuzz.sh +@@ -35,8 +35,10 @@ fi + meson $build -D$fuzzflag -Db_lundef=false + ninja -C $build fuzzers + +-for d in "$(dirname "$0")/../test/fuzz-corpus/"*; do +- zip -jqr $OUT/fuzz-$(basename "$d")_seed_corpus.zip "$d" ++# The seed corpus is a separate flat archive for each fuzzer, ++# with a fixed name ${fuzzer}_seed_corpus.zip. ++for d in "$(dirname "$0")/../test/fuzz/fuzz-"*; do ++ zip -jqr $OUT/$(basename "$d")_seed_corpus.zip "$d" + done + + # get fuzz-dns-packet corpus diff --git a/SOURCES/0152-test-add-testcase-for-issue-10007-by-oss-fuzz.patch b/SOURCES/0152-test-add-testcase-for-issue-10007-by-oss-fuzz.patch new file mode 100644 index 0000000..23efc15 --- /dev/null +++ b/SOURCES/0152-test-add-testcase-for-issue-10007-by-oss-fuzz.patch @@ -0,0 +1,37 @@ +From 684f05b8d7d491c7323302f35f4e28418732e558 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 22 Aug 2018 12:39:40 +0900 +Subject: [PATCH] test: add testcase for issue 10007 by oss-fuzz + +(cherry picked from commit a1a605f144e5635fdae57125a92032b3e5ebeca9) + +Resolves: #1696224 +--- + test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 | 6 ++++++ + test/fuzz-regressions/meson.build | 1 + + 2 files changed, 7 insertions(+) + create mode 100644 test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 + +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 b/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 +new file mode 100644 +index 0000000000..893630c83f +--- /dev/null ++++ b/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 +@@ -0,0 +1,6 @@ ++socket ++ # ++[Socket] ++ListenStream=vsock u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H5%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%HHs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%HHs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fus-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0s-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%u%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%s-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H23372036708:255 ++ListenStream=vsock:34843013755:210 ++Bis0%Hs-fu%H%H0%Hs-fu%H%H0%Hs-fu%H%H0%s-fu/H%H0%Hs-fu%H%H32767%Hs-fu%H%H0%Hs-fu%H%H0%Hs-f +\ No newline at end of file +diff --git a/test/fuzz-regressions/meson.build b/test/fuzz-regressions/meson.build +index 776ee07f67..b98436a4af 100644 +--- a/test/fuzz-regressions/meson.build ++++ b/test/fuzz-regressions/meson.build +@@ -37,4 +37,5 @@ fuzz_regression_tests = ''' + fuzz-unit-file/oss-fuzz-7004 + fuzz-unit-file/oss-fuzz-8064 + fuzz-unit-file/oss-fuzz-8827 ++ fuzz-unit-file/oss-fuzz-10007 + '''.split() diff --git a/SOURCES/0153-fuzz-unify-the-fuzz-regressions-directory-with-the-m.patch b/SOURCES/0153-fuzz-unify-the-fuzz-regressions-directory-with-the-m.patch new file mode 100644 index 0000000..a95a8bb --- /dev/null +++ b/SOURCES/0153-fuzz-unify-the-fuzz-regressions-directory-with-the-m.patch @@ -0,0 +1,240 @@ +From 338519bd5d676d3f7bb5d58f4dac9fb6814afa78 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 7 Jul 2018 18:09:21 +0200 +Subject: [PATCH] fuzz: unify the "fuzz-regressions" directory with the main + corpus + +There isn't really much need to keep them separate. Anything which is a good +corpus entry can be used as a smoke test, and anything which which is a +regression test can just as well be inserted into the corpus. + +The only functional difference from this patch (apart from different paths in +output) is that the regression tests are now zipped together with the rest of +the corpus. + +$ meson configure build -Dslow-tests=true && ninja -C build test +... +307/325 fuzz-dns-packet:issue-7888:address OK 0.06 s +308/325 fuzz-dns-packet:oss-fuzz-5465:address OK 0.04 s +309/325 fuzz-journal-remote:crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76:address OK 0.07 s +310/325 fuzz-journal-remote:crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45:address OK 0.05 s +311/325 fuzz-journal-remote:oss-fuzz-8659:address OK 0.05 s +312/325 fuzz-journal-remote:oss-fuzz-8686:address OK 0.07 s +313/325 fuzz-unit-file:oss-fuzz-6884:address OK 0.06 s +314/325 fuzz-unit-file:oss-fuzz-6885:address OK 0.05 s +315/325 fuzz-unit-file:oss-fuzz-6886:address OK 0.05 s +316/325 fuzz-unit-file:oss-fuzz-6892:address OK 0.05 s +317/325 fuzz-unit-file:oss-fuzz-6897:address OK 0.05 s +318/325 fuzz-unit-file:oss-fuzz-6897-evverx:address OK 0.06 s +319/325 fuzz-unit-file:oss-fuzz-6908:address OK 0.07 s +320/325 fuzz-unit-file:oss-fuzz-6917:address OK 0.07 s +321/325 fuzz-unit-file:oss-fuzz-6977:address OK 0.13 s +322/325 fuzz-unit-file:oss-fuzz-6977-unminimized:address OK 0.12 s +323/325 fuzz-unit-file:oss-fuzz-7004:address OK 0.05 s +324/325 fuzz-unit-file:oss-fuzz-8064:address OK 0.05 s +325/325 fuzz-unit-file:oss-fuzz-8827:address OK 0.52 s + +(cherry picked from commit c74a3f973e3e0bac13d66a28728a47f10046b71f) + +Resolves: #1696224 +--- + meson.build | 4 +--- + test/fuzz-regressions/.gitattributes | 1 - + .../fuzz-dns-packet/issue-7888 | Bin + .../fuzz-dns-packet/oss-fuzz-5465 | Bin + .../crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 | Bin + .../crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 | Bin + .../fuzz-journal-remote/oss-fuzz-8659 | 0 + .../fuzz-journal-remote/oss-fuzz-8686 | 0 + .../fuzz-journald-syslog/github-9795 | 0 + .../fuzz-journald-syslog/github-9820 | 0 + .../fuzz-journald-syslog/github-9827 | 0 + .../fuzz-journald-syslog/github-9829 | 0 + ...timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 | Bin + ...timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b | Bin + .../fuzz-unit-file/oss-fuzz-10007 | 0 + .../fuzz-unit-file/oss-fuzz-6884 | 0 + .../fuzz-unit-file/oss-fuzz-6885 | 0 + .../fuzz-unit-file/oss-fuzz-6886 | 0 + .../fuzz-unit-file/oss-fuzz-6892 | 0 + .../fuzz-unit-file/oss-fuzz-6897 | 0 + .../fuzz-unit-file/oss-fuzz-6897-evverx | 0 + .../fuzz-unit-file/oss-fuzz-6908 | 0 + .../fuzz-unit-file/oss-fuzz-6917 | 0 + .../fuzz-unit-file/oss-fuzz-6977 | 0 + .../fuzz-unit-file/oss-fuzz-6977-unminimized | 0 + .../fuzz-unit-file/oss-fuzz-7004 | 0 + .../fuzz-unit-file/oss-fuzz-8064 | 0 + .../fuzz-unit-file/oss-fuzz-8827 | 0 + test/{fuzz-regressions => fuzz}/meson.build | 0 + test/meson.build | 2 +- + 30 files changed, 2 insertions(+), 5 deletions(-) + delete mode 100644 test/fuzz-regressions/.gitattributes + rename test/{fuzz-regressions => fuzz}/fuzz-dns-packet/issue-7888 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-dns-packet/oss-fuzz-5465 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journal-remote/oss-fuzz-8659 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journal-remote/oss-fuzz-8686 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journald-syslog/github-9795 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journald-syslog/github-9820 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journald-syslog/github-9827 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-journald-syslog/github-9829 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-10007 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6884 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6885 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6886 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6892 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6897 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6897-evverx (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6908 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6917 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6977 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-6977-unminimized (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-7004 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-8064 (100%) + rename test/{fuzz-regressions => fuzz}/fuzz-unit-file/oss-fuzz-8827 (100%) + rename test/{fuzz-regressions => fuzz}/meson.build (100%) + +diff --git a/meson.build b/meson.build +index f2d67b7e02..709597e5c4 100644 +--- a/meson.build ++++ b/meson.build +@@ -2777,9 +2777,7 @@ foreach tuple : sanitizers + test('@0@:@1@:@2@'.format(b, c, sanitizer), + env, + args : [exe.full_path(), +- join_paths(meson.source_root(), +- 'test/fuzz-regressions', +- p)]) ++ join_paths(meson.source_root(), 'test/fuzz', p)]) + endif + endforeach + endif +diff --git a/test/fuzz-regressions/.gitattributes b/test/fuzz-regressions/.gitattributes +deleted file mode 100644 +index 7b1b3e1835..0000000000 +--- a/test/fuzz-regressions/.gitattributes ++++ /dev/null +@@ -1 +0,0 @@ +-/*/* -whitespace +diff --git a/test/fuzz-regressions/fuzz-dns-packet/issue-7888 b/test/fuzz/fuzz-dns-packet/issue-7888 +similarity index 100% +rename from test/fuzz-regressions/fuzz-dns-packet/issue-7888 +rename to test/fuzz/fuzz-dns-packet/issue-7888 +diff --git a/test/fuzz-regressions/fuzz-dns-packet/oss-fuzz-5465 b/test/fuzz/fuzz-dns-packet/oss-fuzz-5465 +similarity index 100% +rename from test/fuzz-regressions/fuzz-dns-packet/oss-fuzz-5465 +rename to test/fuzz/fuzz-dns-packet/oss-fuzz-5465 +diff --git a/test/fuzz-regressions/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 b/test/fuzz/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 +rename to test/fuzz/fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 +diff --git a/test/fuzz-regressions/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 b/test/fuzz/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 +rename to test/fuzz/fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 +diff --git a/test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8659 b/test/fuzz/fuzz-journal-remote/oss-fuzz-8659 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8659 +rename to test/fuzz/fuzz-journal-remote/oss-fuzz-8659 +diff --git a/test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8686 b/test/fuzz/fuzz-journal-remote/oss-fuzz-8686 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journal-remote/oss-fuzz-8686 +rename to test/fuzz/fuzz-journal-remote/oss-fuzz-8686 +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9795 b/test/fuzz/fuzz-journald-syslog/github-9795 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journald-syslog/github-9795 +rename to test/fuzz/fuzz-journald-syslog/github-9795 +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9820 b/test/fuzz/fuzz-journald-syslog/github-9820 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journald-syslog/github-9820 +rename to test/fuzz/fuzz-journald-syslog/github-9820 +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9827 b/test/fuzz/fuzz-journald-syslog/github-9827 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journald-syslog/github-9827 +rename to test/fuzz/fuzz-journald-syslog/github-9827 +diff --git a/test/fuzz-regressions/fuzz-journald-syslog/github-9829 b/test/fuzz/fuzz-journald-syslog/github-9829 +similarity index 100% +rename from test/fuzz-regressions/fuzz-journald-syslog/github-9829 +rename to test/fuzz/fuzz-journald-syslog/github-9829 +diff --git a/test/fuzz-regressions/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 b/test/fuzz/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 +similarity index 100% +rename from test/fuzz-regressions/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 +rename to test/fuzz/fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 +diff --git a/test/fuzz-regressions/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b b/test/fuzz/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b +similarity index 100% +rename from test/fuzz-regressions/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b +rename to test/fuzz/fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 b/test/fuzz/fuzz-unit-file/oss-fuzz-10007 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-10007 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-10007 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6884 b/test/fuzz/fuzz-unit-file/oss-fuzz-6884 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6884 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6884 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6885 b/test/fuzz/fuzz-unit-file/oss-fuzz-6885 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6885 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6885 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6886 b/test/fuzz/fuzz-unit-file/oss-fuzz-6886 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6886 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6886 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6892 b/test/fuzz/fuzz-unit-file/oss-fuzz-6892 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6892 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6892 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897 b/test/fuzz/fuzz-unit-file/oss-fuzz-6897 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6897 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897-evverx b/test/fuzz/fuzz-unit-file/oss-fuzz-6897-evverx +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6897-evverx +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6897-evverx +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6908 b/test/fuzz/fuzz-unit-file/oss-fuzz-6908 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6908 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6908 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6917 b/test/fuzz/fuzz-unit-file/oss-fuzz-6917 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6917 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6917 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977 b/test/fuzz/fuzz-unit-file/oss-fuzz-6977 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6977 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977-unminimized b/test/fuzz/fuzz-unit-file/oss-fuzz-6977-unminimized +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-6977-unminimized +rename to test/fuzz/fuzz-unit-file/oss-fuzz-6977-unminimized +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-7004 b/test/fuzz/fuzz-unit-file/oss-fuzz-7004 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-7004 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-7004 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8064 b/test/fuzz/fuzz-unit-file/oss-fuzz-8064 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8064 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-8064 +diff --git a/test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8827 b/test/fuzz/fuzz-unit-file/oss-fuzz-8827 +similarity index 100% +rename from test/fuzz-regressions/fuzz-unit-file/oss-fuzz-8827 +rename to test/fuzz/fuzz-unit-file/oss-fuzz-8827 +diff --git a/test/fuzz-regressions/meson.build b/test/fuzz/meson.build +similarity index 100% +rename from test/fuzz-regressions/meson.build +rename to test/fuzz/meson.build +diff --git a/test/meson.build b/test/meson.build +index 826e684e59..fb9f2cdb9b 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -244,4 +244,4 @@ if conf.get('ENABLE_HWDB') == 1 + timeout : 90) + endif + +-subdir('fuzz-regressions') ++subdir('fuzz') diff --git a/SOURCES/0154-test-bus-marshal-use-cescaping-instead-of-hexmem.patch b/SOURCES/0154-test-bus-marshal-use-cescaping-instead-of-hexmem.patch new file mode 100644 index 0000000..6509a94 --- /dev/null +++ b/SOURCES/0154-test-bus-marshal-use-cescaping-instead-of-hexmem.patch @@ -0,0 +1,50 @@ +From 4c6c21669483a38cf2a387784a3881b3a47139a3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 7 Jul 2018 19:08:52 +0200 +Subject: [PATCH] test-bus-marshal: use cescaping instead of hexmem + +It is easier to see the contents this way by eye. + +(cherry picked from commit 3ddf3d439463ab2c76391a4d22b54166be2dbe94) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/test-bus-marshal.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c +index c647f0ff12..f007168ca6 100644 +--- a/src/libsystemd/sd-bus/test-bus-marshal.c ++++ b/src/libsystemd/sd-bus/test-bus-marshal.c +@@ -20,8 +20,8 @@ + #include "bus-label.h" + #include "bus-message.h" + #include "bus-util.h" ++#include "escape.h" + #include "fd-util.h" +-#include "hexdecoct.h" + #include "log.h" + #include "util.h" + +@@ -112,7 +112,7 @@ int main(int argc, char *argv[]) { + uint8_t u, v; + void *buffer = NULL; + size_t sz; +- char *h; ++ _cleanup_free_ char *h = NULL; + const int32_t integer_array[] = { -1, -2, 0, 1, 2 }, *return_array; + char *s; + _cleanup_free_ char *first = NULL, *second = NULL, *third = NULL; +@@ -194,11 +194,9 @@ int main(int argc, char *argv[]) { + r = bus_message_get_blob(m, &buffer, &sz); + assert_se(r >= 0); + +- h = hexmem(buffer, sz); ++ h = cescape_length(buffer, sz); + assert_se(h); +- + log_info("message size = %zu, contents =\n%s", sz, h); +- free(h); + + #if HAVE_GLIB + #ifndef __SANITIZE_ADDRESS__ diff --git a/SOURCES/0155-meson-add-Dlog-trace-to-set-LOG_TRACE.patch b/SOURCES/0155-meson-add-Dlog-trace-to-set-LOG_TRACE.patch new file mode 100644 index 0000000..baca5af --- /dev/null +++ b/SOURCES/0155-meson-add-Dlog-trace-to-set-LOG_TRACE.patch @@ -0,0 +1,50 @@ +From c405c3035b595970e65ac9586909618525372c45 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 7 Aug 2018 17:34:47 +0200 +Subject: [PATCH] meson: add -Dlog-trace to set LOG_TRACE + +The justification is the same as for -Dvalgrind: setting config in +meson in this way is easier, because when the value is changed stuff +that should be rebuilt is rebuilt. + +(cherry picked from commit fd5dec9adf76591d713f163d43d04e3beb76893e) + +Resolves: #1696224 +--- + meson.build | 2 ++ + meson_options.txt | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/meson.build b/meson.build +index 709597e5c4..c1013d525b 100644 +--- a/meson.build ++++ b/meson.build +@@ -782,6 +782,7 @@ conf.set10('ENABLE_DEBUG_HASHMAP', enable_debug_hashmap) + conf.set10('ENABLE_DEBUG_MMAP_CACHE', enable_debug_mmap_cache) + + conf.set10('VALGRIND', get_option('valgrind')) ++conf.set10('LOG_TRACE', get_option('log-trace')) + + ##################################################################### + +@@ -2993,6 +2994,7 @@ foreach tuple : [ + ['debug hashmap'], + ['debug mmap cache'], + ['valgrind', conf.get('VALGRIND') == 1], ++ ['trace logging', conf.get('LOG_TRACE') == 1], + ] + + if tuple.length() >= 2 +diff --git a/meson_options.txt b/meson_options.txt +index 5716f45ccf..f06a130582 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -52,6 +52,8 @@ option('memory-accounting-default', type : 'boolean', + description : 'enable MemoryAccounting= by default') + option('valgrind', type : 'boolean', value : false, + description : 'do extra operations to avoid valgrind warnings') ++option('log-trace', type : 'boolean', value : false, ++ description : 'enable low level debug logging') + + option('utmp', type : 'boolean', + description : 'support for utmp/wtmp log handling') diff --git a/SOURCES/0156-meson-allow-building-resolved-and-machined-without-n.patch b/SOURCES/0156-meson-allow-building-resolved-and-machined-without-n.patch new file mode 100644 index 0000000..6efe520 --- /dev/null +++ b/SOURCES/0156-meson-allow-building-resolved-and-machined-without-n.patch @@ -0,0 +1,235 @@ +From 1a368abf7a3d72ecda504a69602b33b6869a485c Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 18 Jul 2018 09:25:57 +0900 +Subject: [PATCH] meson: allow building resolved and machined without nss + modules + +This adds -Dnss-resolve= and -Dnss-mymachines= meson options. +By using this option, e.g., resolved can be built without nss-resolve. +When no nss modules are built, then test-nss is neither built. + +Also, This changes the option name -Dmyhostname= to -Dnss-myhostname= +for consistency to other nss related options. + +Closes #9596. + +(cherry picked from commit 08540a9591efe105439be81fc43d6dc65b715978) + +Resolves: #1696224 +--- + man/nss-myhostname.xml | 2 +- + man/nss-mymachines.xml | 2 +- + man/nss-resolve.xml | 2 +- + man/rules/meson.build | 6 +++--- + meson.build | 40 ++++++++++++++++++++++++++++++++++------ + meson_options.txt | 12 ++++++++---- + src/test/meson.build | 2 +- + src/test/test-nss.c | 6 +++--- + 8 files changed, 52 insertions(+), 20 deletions(-) + +diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml +index e1aabacad2..18a6f5f665 100644 +--- a/man/nss-myhostname.xml ++++ b/man/nss-myhostname.xml +@@ -6,7 +6,7 @@ + SPDX-License-Identifier: LGPL-2.1+ + --> + +- ++ + + + nss-myhostname +diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml +index 394a905665..d9811b24cc 100644 +--- a/man/nss-mymachines.xml ++++ b/man/nss-mymachines.xml +@@ -6,7 +6,7 @@ + SPDX-License-Identifier: LGPL-2.1+ + --> + +- ++ + + + nss-mymachines +diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml +index 588bc04976..56c8099e70 100644 +--- a/man/nss-resolve.xml ++++ b/man/nss-resolve.xml +@@ -6,7 +6,7 @@ + SPDX-License-Identifier: LGPL-2.1+ + --> + +- ++ + + + nss-resolve +diff --git a/man/rules/meson.build b/man/rules/meson.build +index 9458a4012d..989d11c9b9 100644 +--- a/man/rules/meson.build ++++ b/man/rules/meson.build +@@ -37,9 +37,9 @@ manpages = [ + ['modules-load.d', '5', [], 'HAVE_KMOD'], + ['networkctl', '1', [], 'ENABLE_NETWORKD'], + ['networkd.conf', '5', ['networkd.conf.d'], 'ENABLE_NETWORKD'], +- ['nss-myhostname', '8', ['libnss_myhostname.so.2'], 'ENABLE_MYHOSTNAME'], +- ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_MACHINED'], +- ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_RESOLVE'], ++ ['nss-myhostname', '8', ['libnss_myhostname.so.2'], 'ENABLE_NSS_MYHOSTNAME'], ++ ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_NSS_MYMACHINES'], ++ ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_NSS_RESOLVE'], + ['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'], + ['os-release', '5', [], ''], + ['pam_systemd', '8', [], 'HAVE_PAM'], +diff --git a/meson.build b/meson.build +index c1013d525b..863e8eb399 100644 +--- a/meson.build ++++ b/meson.build +@@ -1216,7 +1216,6 @@ foreach term : ['utmp', + 'networkd', + 'timedated', + 'timesyncd', +- 'myhostname', + 'firstboot', + 'randomseed', + 'backlight', +@@ -1233,12 +1232,39 @@ foreach term : ['utmp', + 'smack', + 'gshadow', + 'idn', ++ 'nss-myhostname', + 'nss-systemd'] + have = get_option(term) + name = 'ENABLE_' + term.underscorify().to_upper() + conf.set10(name, have) + endforeach + ++foreach tuple : [['nss-mymachines', 'machined'], ++ ['nss-resolve', 'resolve']] ++ want = get_option(tuple[0]) ++ if want != 'false' ++ have = get_option(tuple[1]) ++ if want == 'true' and not have ++ error('@0@ is requested but @1@ is disabled'.format(tuple[0], tuple[1])) ++ endif ++ else ++ have = false ++ endif ++ name = 'ENABLE_' + tuple[0].underscorify().to_upper() ++ conf.set10(name, have) ++endforeach ++ ++enable_nss = false ++foreach term : ['ENABLE_NSS_MYHOSTNAME', ++ 'ENABLE_NSS_MYMACHINES', ++ 'ENABLE_NSS_RESOLVE', ++ 'ENABLE_NSS_SYSTEMD'] ++ if conf.get(term) == 1 ++ enable_nss = true ++ endif ++endforeach ++conf.set10('ENABLE_NSS', enable_nss) ++ + conf.set10('ENABLE_TIMEDATECTL', get_option('timedated') or get_option('timesyncd')) + + want_tests = get_option('tests') +@@ -1417,10 +1443,10 @@ test_dlopen = executable( + link_with : [libbasic], + dependencies : [libdl]) + +-foreach tuple : [['myhostname', 'ENABLE_MYHOSTNAME'], ++foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'], + ['systemd', 'ENABLE_NSS_SYSTEMD'], +- ['mymachines', 'ENABLE_MACHINED'], +- ['resolve', 'ENABLE_RESOLVE']] ++ ['mymachines', 'ENABLE_NSS_MYMACHINES'], ++ ['resolve', 'ENABLE_NSS_RESOLVE']] + + condition = tuple[1] == '' or conf.get(tuple[1]) == 1 + if condition +@@ -2943,7 +2969,6 @@ foreach tuple : [ + ['idn'], + ['libidn2'], + ['libidn'], +- ['nss-systemd'], + ['libiptc'], + ['elfutils'], + ['binfmt'], +@@ -2978,7 +3003,10 @@ foreach tuple : [ + ['blkid'], + ['dbus'], + ['glib'], +- ['nss-myhostname', conf.get('ENABLE_MYHOSTNAME') == 1], ++ ['nss-myhostname', conf.get('ENABLE_NSS_MYHOSTNAME') == 1], ++ ['nss-mymachines', conf.get('ENABLE_NSS_MYMACHINES') == 1], ++ ['nss-resolve', conf.get('ENABLE_NSS_RESOLVE') == 1], ++ ['nss-systemd', conf.get('ENABLE_NSS_SYSTEMD') == 1], + ['hwdb'], + ['tpm'], + ['man pages', want_man], +diff --git a/meson_options.txt b/meson_options.txt +index f06a130582..563b11f0a2 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -91,8 +91,14 @@ option('timesyncd', type : 'boolean', + description : 'install the systemd-timesyncd daemon') + option('remote', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'support for "journal over the network"') +-option('myhostname', type : 'boolean', +- description : 'nss-myhostname support') ++option('nss-myhostname', type : 'boolean', ++ description : 'install nss-myhostname module') ++option('nss-mymachines', type : 'combo', choices : ['auto', 'true', 'false'], ++ description : 'install nss-mymachines module') ++option('nss-resolve', type : 'combo', choices : ['auto', 'true', 'false'], ++ description : 'install nss-resolve module') ++option('nss-systemd', type : 'boolean', ++ description : 'install nss-systemd module') + option('firstboot', type : 'boolean', + description : 'support for firstboot mechanism') + option('randomseed', type : 'boolean', +@@ -249,8 +255,6 @@ option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'libidn2 support') + option('libidn', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'libidn support') +-option('nss-systemd', type : 'boolean', +- description : 'enable nss-systemd') + option('libiptc', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'libiptc support') + option('qrencode', type : 'combo', choices : ['auto', 'true', 'false'], +diff --git a/src/test/meson.build b/src/test/meson.build +index b982251b1f..0998f59897 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -650,7 +650,7 @@ tests += [ + [['src/test/test-nss.c'], + [], + [libdl], +- '', 'manual'], ++ 'ENABLE_NSS', 'manual'], + + [['src/test/test-umount.c', + 'src/core/mount-setup.c', +diff --git a/src/test/test-nss.c b/src/test/test-nss.c +index 9e543e7557..e0e7bb300d 100644 +--- a/src/test/test-nss.c ++++ b/src/test/test-nss.c +@@ -431,13 +431,13 @@ static int parse_argv(int argc, char **argv, + modules = strv_new(argv[1], NULL); + else + modules = strv_new( +-#if ENABLE_MYHOSTNAME ++#if ENABLE_NSS_MYHOSTNAME + "myhostname", + #endif +-#if ENABLE_RESOLVE ++#if ENABLE_NSS_RESOLVE + "resolve", + #endif +-#if ENABLE_MACHINED ++#if ENABLE_NSS_MYMACHINES + "mymachines", + #endif + "dns", diff --git a/SOURCES/0157-meson-drop-duplicated-condition.patch b/SOURCES/0157-meson-drop-duplicated-condition.patch new file mode 100644 index 0000000..51b5c7e --- /dev/null +++ b/SOURCES/0157-meson-drop-duplicated-condition.patch @@ -0,0 +1,33 @@ +From fff862ce6f33acb6ce8dc422e0f53ec681ebed6d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 7 Aug 2018 18:10:53 +0200 +Subject: [PATCH] meson: drop duplicated condition + +The generic check suffices for those four. + +(cherry picked from commit 6bd2bc8e16a6d515f8a21c47fd6b833d7fcfdd1c) + +Resolves: #1696224 +--- + meson.build | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/meson.build b/meson.build +index 863e8eb399..f623bcf37d 100644 +--- a/meson.build ++++ b/meson.build +@@ -3003,10 +3003,10 @@ foreach tuple : [ + ['blkid'], + ['dbus'], + ['glib'], +- ['nss-myhostname', conf.get('ENABLE_NSS_MYHOSTNAME') == 1], +- ['nss-mymachines', conf.get('ENABLE_NSS_MYMACHINES') == 1], +- ['nss-resolve', conf.get('ENABLE_NSS_RESOLVE') == 1], +- ['nss-systemd', conf.get('ENABLE_NSS_SYSTEMD') == 1], ++ ['nss-myhostname'], ++ ['nss-mymachines'], ++ ['nss-resolve'], ++ ['nss-systemd'], + ['hwdb'], + ['tpm'], + ['man pages', want_man], diff --git a/SOURCES/0158-meson-use-.source_root-in-more-places.patch b/SOURCES/0158-meson-use-.source_root-in-more-places.patch new file mode 100644 index 0000000..0cc4b24 --- /dev/null +++ b/SOURCES/0158-meson-use-.source_root-in-more-places.patch @@ -0,0 +1,81 @@ +From 2eca34cfa911fa7c1aafef5d60b7a2e752ae5b3b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 10 Aug 2018 16:50:07 +0200 +Subject: [PATCH] meson: use .source_root() in more places + +In the main meson.build file, .source_root() and .current_source_dir() are +equivalent, but it seems more appropriate to use .source_root() when we are appending +a path which is by design relative to repo root. + +(cherry picked from commit 243e5cecc3a211519544ccba01c44edc827ac517) + +Resolves: #1696224 +--- + meson.build | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/meson.build b/meson.build +index f623bcf37d..fe161e5ec5 100644 +--- a/meson.build ++++ b/meson.build +@@ -1453,7 +1453,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.current_source_dir(), sym) ++ version_script_arg = join_paths(meson.source_root(), sym) + + nss = shared_library( + 'nss_' + module, +@@ -1704,7 +1704,7 @@ if conf.get('ENABLE_LOGIND') == 1 + public_programs += [exe] + + if conf.get('HAVE_PAM') == 1 +- version_script_arg = join_paths(meson.current_source_dir(), pam_systemd_sym) ++ version_script_arg = join_paths(meson.source_root(), pam_systemd_sym) + pam_systemd = shared_library( + 'pam_systemd', + pam_systemd_c, +@@ -2816,7 +2816,7 @@ endforeach + if git.found() + all_files = run_command( + git, +- ['--git-dir=@0@/.git'.format(meson.current_source_dir()), ++ ['--git-dir=@0@/.git'.format(meson.source_root()), + 'ls-files', + ':/*.[ch]']) + all_files = files(all_files.stdout().split()) +@@ -2824,10 +2824,10 @@ if git.found() + custom_target( + 'tags', + output : 'tags', +- command : [env, 'etags', '-o', '@0@/TAGS'.format(meson.current_source_dir())] + all_files) ++ command : [env, 'etags', '-o', '@0@/TAGS'.format(meson.source_root())] + all_files) + run_target( + 'ctags', +- command : [env, 'ctags', '-o', '@0@/tags'.format(meson.current_source_dir())] + all_files) ++ command : [env, 'ctags', '-o', '@0@/tags'.format(meson.source_root())] + all_files) + endif + + if git.found() +@@ -2840,17 +2840,17 @@ endif + if git.found() + git_head = run_command( + git, +- ['--git-dir=@0@/.git'.format(meson.current_source_dir()), ++ ['--git-dir=@0@/.git'.format(meson.source_root()), + 'rev-parse', 'HEAD']).stdout().strip() + git_head_short = run_command( + git, +- ['--git-dir=@0@/.git'.format(meson.current_source_dir()), ++ ['--git-dir=@0@/.git'.format(meson.source_root()), + 'rev-parse', '--short=7', 'HEAD']).stdout().strip() + + run_target( + 'git-snapshot', + command : ['git', 'archive', +- '-o', '@0@/systemd-@1@.tar.gz'.format(meson.current_source_dir(), ++ '-o', '@0@/systemd-@1@.tar.gz'.format(meson.source_root(), + git_head_short), + '--prefix', 'systemd-@0@/'.format(git_head), + 'HEAD']) diff --git a/SOURCES/0159-meson-treat-all-fuzz-cases-as-unit-tests.patch b/SOURCES/0159-meson-treat-all-fuzz-cases-as-unit-tests.patch new file mode 100644 index 0000000..7751f3d --- /dev/null +++ b/SOURCES/0159-meson-treat-all-fuzz-cases-as-unit-tests.patch @@ -0,0 +1,141 @@ +From 6a8c286e1a45dfa64c48cd4d5a911d4f71de9a16 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 10 Aug 2018 17:15:05 +0200 +Subject: [PATCH] meson: treat all fuzz cases as unit tests + +318/365 fuzz-bus-message:crash-26bba7182dedc8848939931d9fcefcb7922f2e56:address OK 0.03 s +319/365 fuzz-bus-message:crash-29ed3c202e0ffade3cad42c8bbeb6cc68a21eb8e:address OK 0.03 s +320/365 fuzz-bus-message:crash-b88ad9ecf4aacf4a0caca5b5543953265367f084:address OK 0.03 s +321/365 fuzz-bus-message:crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e:address OK 0.03 s +322/365 fuzz-bus-message:crash-d8f3941c74219b4c03532c9b244d5ea539c61af5:address OK 0.03 s +323/365 fuzz-bus-message:crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5:address OK 0.03 s +324/365 fuzz-bus-message:leak-c09c0e2256d43bc5e2d02748c8d8760e7bc25d20:address OK 0.04 s +325/365 fuzz-bus-message:message1:address OK 0.03 s +326/365 fuzz-bus-message:timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b:address OK 0.03 s +327/365 fuzz-dhcp-server:discover-existing:address OK 0.04 s +328/365 fuzz-dhcp-server:discover-new:address OK 0.03 s +329/365 fuzz-dhcp-server:release:address OK 0.04 s +330/365 fuzz-dhcp-server:request-existing:address OK 0.03 s +331/365 fuzz-dhcp-server:request-new:address OK 0.03 s +332/365 fuzz-dhcp-server:request-reboot:address OK 0.03 s +333/365 fuzz-dhcp-server:request-renew:address OK 0.03 s +334/365 fuzz-dns-packet:issue-7888:address OK 0.03 s +335/365 fuzz-dns-packet:oss-fuzz-5465:address OK 0.03 s +336/365 fuzz-journal-remote:crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76:address OK 0.06 s +337/365 fuzz-journal-remote:crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45:address OK 0.04 s +338/365 fuzz-journal-remote:invalid-ts.txt:address OK 0.04 s +339/365 fuzz-journal-remote:oss-fuzz-8659:address OK 0.06 s +340/365 fuzz-journal-remote:oss-fuzz-8686:address OK 0.04 s +341/365 fuzz-journal-remote:sample.txt:address OK 0.07 s +342/365 fuzz-unit-file:directives.service:address OK 0.03 s +343/365 fuzz-unit-file:empty.scope:address OK 0.04 s +344/365 fuzz-unit-file:machine.slice:address OK 0.03 s +345/365 fuzz-unit-file:oss-fuzz-6884:address OK 0.05 s +346/365 fuzz-unit-file:oss-fuzz-6885:address OK 0.03 s +347/365 fuzz-unit-file:oss-fuzz-6886:address OK 0.04 s +348/365 fuzz-unit-file:oss-fuzz-6892:address OK 0.03 s +349/365 fuzz-unit-file:oss-fuzz-6897:address OK 0.05 s +350/365 fuzz-unit-file:oss-fuzz-6897-evverx:address OK 0.04 s +351/365 fuzz-unit-file:oss-fuzz-6908:address OK 0.05 s +352/365 fuzz-unit-file:oss-fuzz-6917:address OK 0.06 s +353/365 fuzz-unit-file:oss-fuzz-6977:address OK 0.08 s +354/365 fuzz-unit-file:oss-fuzz-6977-unminimized:address OK 0.10 s +355/365 fuzz-unit-file:oss-fuzz-7004:address OK 0.03 s +356/365 fuzz-unit-file:oss-fuzz-8064:address OK 0.03 s +357/365 fuzz-unit-file:oss-fuzz-8827:address OK 0.50 s +358/365 fuzz-unit-file:proc-sys-fs-binfmt_misc.automount:address OK 0.03 s +359/365 fuzz-unit-file:syslog.socket:address OK 0.03 s +360/365 fuzz-unit-file:systemd-ask-password-console.path:address OK 0.03 s +361/365 fuzz-unit-file:systemd-machined.service:address OK 0.03 s +362/365 fuzz-unit-file:systemd-resolved.service:address OK 0.03 s +363/365 fuzz-unit-file:systemd-tmpfiles-clean.timer:address OK 0.03 s +364/365 fuzz-unit-file:timers.target:address OK 0.03 s +365/365 fuzz-unit-file:var-lib-machines.mount:address OK 0.04 s + +This gives us slightly nicer coverage in the normal test run. + +When in a git repo, git ls-files is used to get a list of files known to git. +This mirrors what update-man-rules does for man files. Only looking at files +known to git makes it easier to not forget to commit the test file to git, +and also makes bisecting easier if some files are left in repo. + +When outside of a git repo, we expect to be unpacked from a tarball, so just +using all files reported by ls is OK. + +(cherry picked from commit e6bad6746151c79a5f408e95714ffa5cea290ab0) + +Resolves: #1696224 +--- + meson.build | 2 +- + test/fuzz/meson.build | 48 ++++++++++++++++++------------------------- + 2 files changed, 21 insertions(+), 29 deletions(-) + +diff --git a/meson.build b/meson.build +index fe161e5ec5..04b461dcd4 100644 +--- a/meson.build ++++ b/meson.build +@@ -2804,7 +2804,7 @@ foreach tuple : sanitizers + test('@0@:@1@:@2@'.format(b, c, sanitizer), + env, + args : [exe.full_path(), +- join_paths(meson.source_root(), 'test/fuzz', p)]) ++ join_paths(meson.source_root(), p)]) + endif + endforeach + endif +diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build +index b98436a4af..56d0f69660 100644 +--- a/test/fuzz/meson.build ++++ b/test/fuzz/meson.build +@@ -11,31 +11,23 @@ sanitize_address = custom_target( + + sanitizers = [['address', sanitize_address]] + +-fuzz_regression_tests = ''' +- fuzz-dns-packet/issue-7888 +- fuzz-dns-packet/oss-fuzz-5465 +- fuzz-journal-remote/crash-5a8f03d4c3a46fcded39527084f437e8e4b54b76 +- fuzz-journal-remote/crash-96dee870ea66d03e89ac321eee28ea63a9b9aa45 +- fuzz-journal-remote/oss-fuzz-8659 +- fuzz-journal-remote/oss-fuzz-8686 +- fuzz-journald-syslog/github-9795 +- fuzz-journald-syslog/github-9820 +- fuzz-journald-syslog/github-9827 +- fuzz-journald-syslog/github-9829 +- fuzz-ndisc-rs/timeout-2815b773c712fa33bea62f541dfa3017c64ea2f1 +- fuzz-ndisc-rs/timeout-61fff7fd1e5dcc07e1b656baab29065ce634ad5b +- fuzz-unit-file/oss-fuzz-6884 +- fuzz-unit-file/oss-fuzz-6885 +- fuzz-unit-file/oss-fuzz-6886 +- fuzz-unit-file/oss-fuzz-6892 +- fuzz-unit-file/oss-fuzz-6897 +- fuzz-unit-file/oss-fuzz-6897-evverx +- fuzz-unit-file/oss-fuzz-6908 +- fuzz-unit-file/oss-fuzz-6917 +- fuzz-unit-file/oss-fuzz-6977 +- fuzz-unit-file/oss-fuzz-6977-unminimized +- fuzz-unit-file/oss-fuzz-7004 +- fuzz-unit-file/oss-fuzz-8064 +- fuzz-unit-file/oss-fuzz-8827 +- fuzz-unit-file/oss-fuzz-10007 +-'''.split() ++if git.found() ++ out = run_command( ++ git, ++ '--git-dir=@0@/.git'.format(meson.source_root()), ++ 'ls-files', ':/test/fuzz/*/*') ++else ++ out = run_command( ++ 'sh', '-c', 'ls @0@/*/*'.format(meson.current_source_dir())) ++endif ++ ++fuzz_regression_tests = [] ++foreach p : out.stdout().split() ++ # Remove the last entry which is ''. ++ # ++ # Also, backslashes get mangled, so skip test. See ++ # https://github.com/mesonbuild/meson/issues/1564. ++ if not p.contains('\\') ++ fuzz_regression_tests += p ++ endif ++endforeach diff --git a/SOURCES/0160-fuzz-bus-message-add-fuzzer-for-message-parsing.patch b/SOURCES/0160-fuzz-bus-message-add-fuzzer-for-message-parsing.patch new file mode 100644 index 0000000..57678f6 --- /dev/null +++ b/SOURCES/0160-fuzz-bus-message-add-fuzzer-for-message-parsing.patch @@ -0,0 +1,102 @@ +From 9a6a36b44ad131036fef5c91edc86c842c9821ba Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 7 Jul 2018 19:30:25 +0200 +Subject: [PATCH] fuzz-bus-message: add fuzzer for message parsing + +As with other fuzzers, SYSTEMD_FUZZ_OUTPUT=1 and SYSTEMD_LOG_LEVEL=debug can be +used for debugging. + +(cherry picked from commit 56b560c26339c4b282c06038316a91509eae75fd) + +Resolves: #1696224 +--- + src/fuzz/fuzz-bus-message.c | 47 ++++++++++++++++++++++++++++ + src/fuzz/meson.build | 4 +++ + test/fuzz/fuzz-bus-message/message1 | Bin 0 -> 534 bytes + 3 files changed, 51 insertions(+) + create mode 100644 src/fuzz/fuzz-bus-message.c + create mode 100644 test/fuzz/fuzz-bus-message/message1 + +diff --git a/src/fuzz/fuzz-bus-message.c b/src/fuzz/fuzz-bus-message.c +new file mode 100644 +index 0000000000..9842c62a6f +--- /dev/null ++++ b/src/fuzz/fuzz-bus-message.c +@@ -0,0 +1,47 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++#include ++ ++#include "alloc-util.h" ++#include "bus-dump.h" ++#include "bus-message.h" ++#include "env-util.h" ++#include "fd-util.h" ++#include "fuzz.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ _cleanup_free_ char *out = NULL; /* out should be freed after g */ ++ size_t out_size; ++ _cleanup_fclose_ FILE *g = NULL; ++ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; ++ _cleanup_free_ void *buffer = NULL; ++ int r; ++ ++ /* We don't want to fill the logs with messages about parse errors. ++ * Disable most logging if not running standalone */ ++ if (!getenv("SYSTEMD_LOG_LEVEL")) ++ log_set_max_level(LOG_CRIT); ++ ++ r = sd_bus_new(&bus); ++ assert_se(r >= 0); ++ ++ assert_se(buffer = memdup(data, size)); ++ ++ r = bus_message_from_malloc(bus, buffer, size, NULL, 0, NULL, &m); ++ if (r == -EBADMSG) ++ return 0; ++ assert_se(r >= 0); ++ TAKE_PTR(buffer); ++ ++ if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0) ++ assert_se(g = open_memstream(&out, &out_size)); ++ ++ bus_message_dump(m, g ?: stdout, BUS_MESSAGE_DUMP_WITH_HEADER); ++ ++ r = sd_bus_message_rewind(m, true); ++ assert_se(r >= 0); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 5c81ac0c5b..1dbe28e57e 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -1,6 +1,10 @@ + # SPDX-License-Identifier: LGPL-2.1+ + + fuzzers += [ ++ [['src/fuzz/fuzz-bus-message.c'], ++ [libshared], ++ []], ++ + [['src/fuzz/fuzz-dns-packet.c', + dns_type_headers], + [libsystemd_resolve_core, +diff --git a/test/fuzz/fuzz-bus-message/message1 b/test/fuzz/fuzz-bus-message/message1 +new file mode 100644 +index 0000000000000000000000000000000000000000..2df70fd7cb6f0e632c4d5c2358091309a5cd3edc +GIT binary patch +literal 534 +zcmZ{h!A`?442GSJjTUi2h$EV`OM6*iyZ|>&NW6m6ZC#~`RCNGV2*icg27V{4hLEuI +z*Z%6nv6IG-c{fDW8PO*Z8RG~@1*A4LLPziq^|n=>fKTCf&ROnOFWhXL{-6KzKQR>* +zA}kdo{MtXi^_lPUKI=U`x#dhG*Hq0w(L%415E=fPT+(I2*knx96tO2RVnnVP6o! +Yuz#WfEX)Cqd!b_JHzYppZsXhk08nC8%>V!Z + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0161-bus-message-use-structured-initialization-to-avoid-u.patch b/SOURCES/0161-bus-message-use-structured-initialization-to-avoid-u.patch new file mode 100644 index 0000000..80b4a78 --- /dev/null +++ b/SOURCES/0161-bus-message-use-structured-initialization-to-avoid-u.patch @@ -0,0 +1,115 @@ +From a82cf4abc81722706b4466e65c1a05f997cf9fdc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 9 Jul 2018 07:38:10 +0200 +Subject: [PATCH] bus-message: use structured initialization to avoid use of + unitialized memory + +As far as I can see, we would either reuse some values from a previously exited +container or just random bytes from the heap. + +Should fix #10127. + +(cherry picked from commit cf81c68e96aa29d0c28b5d3a26d1de9aa1b53b85) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 59 +++++++++++++---------------- + 1 file changed, 27 insertions(+), 32 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 780c8c6185..7f87d018fb 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -1956,7 +1956,7 @@ _public_ int sd_bus_message_open_container( + char type, + const char *contents) { + +- struct bus_container *c, *w; ++ struct bus_container *c; + uint32_t *array_size = NULL; + _cleanup_free_ char *signature = NULL; + size_t before, begin = 0; +@@ -2001,17 +2001,14 @@ _public_ int sd_bus_message_open_container( + return r; + + /* OK, let's fill it in */ +- w = m->containers + m->n_containers++; +- w->enclosing = type; +- w->signature = TAKE_PTR(signature); +- w->peeked_signature = NULL; +- w->index = 0; +- w->array_size = array_size; +- w->before = before; +- w->begin = begin; +- w->n_offsets = w->offsets_allocated = 0; +- w->offsets = NULL; +- w->need_offsets = need_offsets; ++ m->containers[m->n_containers++] = (struct bus_container) { ++ .enclosing = type, ++ .signature = TAKE_PTR(signature), ++ .array_size = array_size, ++ .before = before, ++ .begin = begin, ++ .need_offsets = need_offsets, ++ }; + + return 0; + } +@@ -3980,10 +3977,10 @@ static int bus_message_enter_dict_entry( + _public_ int sd_bus_message_enter_container(sd_bus_message *m, + char type, + const char *contents) { +- struct bus_container *c, *w; ++ struct bus_container *c; + uint32_t *array_size = NULL; + _cleanup_free_ char *signature = NULL; +- size_t before; ++ size_t before, end; + _cleanup_free_ size_t *offsets = NULL; + size_t n_offsets = 0, item_size = 0; + int r; +@@ -4062,28 +4059,26 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m, + return r; + + /* OK, let's fill it in */ +- w = m->containers + m->n_containers++; +- w->enclosing = type; +- w->signature = TAKE_PTR(signature); +- w->peeked_signature = NULL; +- w->index = 0; +- +- w->before = before; +- w->begin = m->rindex; +- +- /* Unary type has fixed size of 1, but virtual size of 0 */ + if (BUS_MESSAGE_IS_GVARIANT(m) && + type == SD_BUS_TYPE_STRUCT && + isempty(signature)) +- w->end = m->rindex + 0; ++ end = m->rindex + 0; + else +- w->end = m->rindex + c->item_size; +- +- w->array_size = array_size; +- w->item_size = item_size; +- w->offsets = TAKE_PTR(offsets); +- w->n_offsets = n_offsets; +- w->offset_index = 0; ++ end = m->rindex + c->item_size; ++ ++ m->containers[m->n_containers++] = (struct bus_container) { ++ .enclosing = type, ++ .signature = TAKE_PTR(signature), ++ ++ .before = before, ++ .begin = m->rindex, ++ /* Unary type has fixed size of 1, but virtual size of 0 */ ++ .end = end, ++ .array_size = array_size, ++ .item_size = item_size, ++ .offsets = TAKE_PTR(offsets), ++ .n_offsets = n_offsets, ++ }; + + return 1; + } diff --git a/SOURCES/0162-bus-message-avoid-an-infinite-loop-on-empty-structur.patch b/SOURCES/0162-bus-message-avoid-an-infinite-loop-on-empty-structur.patch new file mode 100644 index 0000000..e821357 --- /dev/null +++ b/SOURCES/0162-bus-message-avoid-an-infinite-loop-on-empty-structur.patch @@ -0,0 +1,176 @@ +From a2da2b45ac05ef91074e90097115e8c734ca0f64 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 9 Jul 2018 10:52:51 +0200 +Subject: [PATCH] bus-message: avoid an infinite loop on empty structures + +The alternative would be to treat gvariant and !gvariant messages differently. +But this is a problem because we check signatures is variuos places before we +have an actual message, for example in sd_bus_add_object_vtable(). It seems +better to treat things consistent (i.e. follow the lowest common denominator) +and disallow empty structures everywhere. + +(cherry picked from commit ec6bda56cbca9509b1abde1122645630caca877c) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 3 ++- + src/libsystemd/sd-bus/bus-signature.c | 6 ++++++ + src/libsystemd/sd-bus/test-bus-gvariant.c | 6 +++--- + src/libsystemd/sd-bus/test-bus-marshal.c | 6 +++--- + src/libsystemd/sd-bus/test-bus-signature.c | 8 ++++---- + ...crash-26bba7182dedc8848939931d9fcefcb7922f2e56 | Bin 0 -> 157 bytes + ...meout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b | Bin 0 -> 534 bytes + 7 files changed, 18 insertions(+), 11 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56 + create mode 100644 test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 7f87d018fb..1d06fcb80e 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -4178,6 +4178,7 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + + /* signature_element_length does verification internally */ + ++ /* The array element must not be empty */ + assert(l >= 1); + if (free_and_strndup(&c->peeked_signature, + c->signature + c->index + 1, l) < 0) +@@ -4201,7 +4202,7 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + if (r < 0) + return r; + +- assert(l >= 2); ++ assert(l >= 3); + if (free_and_strndup(&c->peeked_signature, + c->signature + c->index + 1, l - 2) < 0) + return -ENOMEM; +diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c +index 18c91e8707..1ca37cbb5a 100644 +--- a/src/libsystemd/sd-bus/bus-signature.c ++++ b/src/libsystemd/sd-bus/bus-signature.c +@@ -58,6 +58,12 @@ static int signature_element_length_internal( + p += t; + } + ++ if (p - s < 2) ++ /* D-Bus spec: Empty structures are not allowed; there ++ * must be at least one type code between the parentheses. ++ */ ++ return -EINVAL; ++ + *l = p - s + 1; + return 0; + } +diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c +index 75804f3458..e606970a3a 100644 +--- a/src/libsystemd/sd-bus/test-bus-gvariant.c ++++ b/src/libsystemd/sd-bus/test-bus-gvariant.c +@@ -19,7 +19,7 @@ + + static void test_bus_gvariant_is_fixed_size(void) { + assert_se(bus_gvariant_is_fixed_size("") > 0); +- assert_se(bus_gvariant_is_fixed_size("()") > 0); ++ assert_se(bus_gvariant_is_fixed_size("()") == -EINVAL); + assert_se(bus_gvariant_is_fixed_size("y") > 0); + assert_se(bus_gvariant_is_fixed_size("u") > 0); + assert_se(bus_gvariant_is_fixed_size("b") > 0); +@@ -44,7 +44,7 @@ static void test_bus_gvariant_is_fixed_size(void) { + + static void test_bus_gvariant_get_size(void) { + assert_se(bus_gvariant_get_size("") == 0); +- assert_se(bus_gvariant_get_size("()") == 1); ++ assert_se(bus_gvariant_get_size("()") == -EINVAL); + assert_se(bus_gvariant_get_size("y") == 1); + assert_se(bus_gvariant_get_size("u") == 4); + assert_se(bus_gvariant_get_size("b") == 1); +@@ -76,7 +76,7 @@ static void test_bus_gvariant_get_size(void) { + + static void test_bus_gvariant_get_alignment(void) { + assert_se(bus_gvariant_get_alignment("") == 1); +- assert_se(bus_gvariant_get_alignment("()") == 1); ++ assert_se(bus_gvariant_get_alignment("()") == -EINVAL); + assert_se(bus_gvariant_get_alignment("y") == 1); + assert_se(bus_gvariant_get_alignment("b") == 1); + assert_se(bus_gvariant_get_alignment("u") == 4); +diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c +index f007168ca6..1743b1b491 100644 +--- a/src/libsystemd/sd-bus/test-bus-marshal.c ++++ b/src/libsystemd/sd-bus/test-bus-marshal.c +@@ -151,7 +151,7 @@ int main(int argc, char *argv[]) { + assert_se(r >= 0); + + r = sd_bus_message_append(m, "()"); +- assert_se(r >= 0); ++ assert_se(r == -EINVAL); + + r = sd_bus_message_append(m, "ba(ss)", 255, 3, "aaa", "1", "bbb", "2", "ccc", "3"); + assert_se(r >= 0); +@@ -293,7 +293,7 @@ int main(int argc, char *argv[]) { + assert_se(v == 10); + + r = sd_bus_message_read(m, "()"); +- assert_se(r > 0); ++ assert_se(r < 0); + + r = sd_bus_message_read(m, "ba(ss)", &boolean, 3, &x, &y, &a, &b, &c, &d); + assert_se(r > 0); +@@ -374,7 +374,7 @@ int main(int argc, char *argv[]) { + + assert_se(sd_bus_message_verify_type(m, 'a', "{yv}") > 0); + +- r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y()"); ++ r = sd_bus_message_skip(m, "a{yv}y(ty)y(yt)y"); + assert_se(r >= 0); + + assert_se(sd_bus_message_verify_type(m, 'b', NULL) > 0); +diff --git a/src/libsystemd/sd-bus/test-bus-signature.c b/src/libsystemd/sd-bus/test-bus-signature.c +index 1ba1909198..a716cd1b35 100644 +--- a/src/libsystemd/sd-bus/test-bus-signature.c ++++ b/src/libsystemd/sd-bus/test-bus-signature.c +@@ -16,9 +16,9 @@ int main(int argc, char *argv[]) { + assert_se(signature_is_single("v", false)); + assert_se(signature_is_single("as", false)); + assert_se(signature_is_single("(ss)", false)); +- assert_se(signature_is_single("()", false)); +- assert_se(signature_is_single("(()()()()())", false)); +- assert_se(signature_is_single("(((())))", false)); ++ assert_se(!signature_is_single("()", false)); ++ assert_se(!signature_is_single("(()()()()())", false)); ++ assert_se(!signature_is_single("(((())))", false)); + assert_se(signature_is_single("((((s))))", false)); + assert_se(signature_is_single("{ss}", true)); + assert_se(signature_is_single("a{ss}", false)); +@@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { + assert_se(signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaas", false)); + assert_se(!signature_is_valid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaau", false)); + +- assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false)); ++ assert_se(signature_is_valid("((((((((((((((((((((((((((((((((s))))))))))))))))))))))))))))))))", false)); + assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false)); + + assert_se(namespace_complex_pattern("", "")); +diff --git a/test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56 b/test/fuzz/fuzz-bus-message/crash-26bba7182dedc8848939931d9fcefcb7922f2e56 +new file mode 100644 +index 0000000000000000000000000000000000000000..f1bf3229effc982c8b129182fe60739efe3c5013 +GIT binary patch +literal 157 +mcmd1#|DTC5gMmSS0SHWtK_neOii>$FgGM4Akdcw0DF6TjSP;el + +literal 0 +HcmV?d00001 + +diff --git a/test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b b/test/fuzz/fuzz-bus-message/timeout-08ee8f6446a4064db064e8e0b3d220147f7d0b5b +new file mode 100644 +index 0000000000000000000000000000000000000000..c975f906eef521a3cfac5627c8b371ee55aa0e6c +GIT binary patch +literal 534 +zcmcJL!Ab-%42J(Y?m8o$d;nSS(q4Ae_Yi!A47)oFEOwaGU5e<<_x8`!K@h}~fslMn +zn)c7Z!M!`6y9Pc0I2S?0hHh3l#W~|szZ;Ct$XAT}7+V?FCpm1RoiBemuU&_Ys%S@7 +zdCkYS>{AZe=OjL~Ie67zrCwgdYud(O^J==RG>!dpXFS^tlZIX@tK0h@{D4MV@hJsW +zyR)R1zXEs6tHM*H04&I}2-7)y>9oEVCxw(Vn{LBxXmJ)=frMcRdZlJ-~v +b#4gh=OPF@NW^U~wGO=l?@b9nvy +Date: Mon, 9 Jul 2018 11:12:33 +0200 +Subject: [PATCH] bus-message: let's always use -EBADMSG when the message is + bad + +-EINVAL means the arguments were somehow wrong, so translate the code we get +internally into -EBADMSG when returning. + +(cherry picked from commit 69bd42ca072dfb2f7603b1f82053063293ab54b5) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 2 ++ + .../crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e | Bin 0 -> 93 bytes + 2 files changed, 2 insertions(+) + create mode 100644 test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 1d06fcb80e..83f17436a1 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -5414,6 +5414,8 @@ int bus_message_parse_fields(sd_bus_message *m) { + &m->root_container.item_size, + &m->root_container.offsets, + &m->root_container.n_offsets); ++ if (r == -EINVAL) ++ return -EBADMSG; + if (r < 0) + return r; + } +diff --git a/test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e b/test/fuzz/fuzz-bus-message/crash-c1b37b4729b42c0c05b23cba4eed5d8102498a1e +new file mode 100644 +index 0000000000000000000000000000000000000000..2ae1a8715a12c65fba27d8e60216112a99b0ace7 +GIT binary patch +literal 93 +wcmd1FDP>|PH8L_f3B<@i03SeB2xg~!`?q0o*WZ8t85 +Date: Tue, 24 Jul 2018 20:14:39 +0200 +Subject: [PATCH] bus-message: rename function for clarity + +There's already message_free_last_container(), so rename to match. + +(cherry picked from commit 9c65778d614588d21645163dea97a5fe2c1c4ca5) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 44 ++++++++++++++--------------- + 1 file changed, 22 insertions(+), 22 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 83f17436a1..7392a43a19 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -77,7 +77,7 @@ static void message_reset_parts(sd_bus_message *m) { + m->cached_rindex_part_begin = 0; + } + +-static struct bus_container *message_get_container(sd_bus_message *m) { ++static struct bus_container *message_get_last_container(sd_bus_message *m) { + assert(m); + + if (m->n_containers == 0) +@@ -90,7 +90,7 @@ static struct bus_container *message_get_container(sd_bus_message *m) { + static void message_free_last_container(sd_bus_message *m) { + struct bus_container *c; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + free(c->signature); + free(c->peeked_signature); +@@ -1220,7 +1220,7 @@ static int message_add_offset(sd_bus_message *m, size_t offset) { + /* Add offset to current container, unless this is the first + * item in it, which will have the 0 offset, which we can + * ignore. */ +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (!c->need_offsets) + return 0; +@@ -1392,7 +1392,7 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void + assert_return(bus_type_is_basic(type), -EINVAL); + assert_return(!m->poisoned, -ESTALE); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ +@@ -1585,7 +1585,7 @@ _public_ int sd_bus_message_append_string_space( + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ +@@ -1974,7 +1974,7 @@ _public_ int sd_bus_message_open_container( + return -ENOMEM; + } + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + signature = strdup(contents); + if (!signature) { +@@ -2199,7 +2199,7 @@ _public_ int sd_bus_message_close_container(sd_bus_message *m) { + assert_return(m->n_containers > 0, -EINVAL); + assert_return(!m->poisoned, -ESTALE); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + if (c->signature && c->signature[c->index] != 0) +@@ -2703,7 +2703,7 @@ _public_ int sd_bus_message_append_string_memfd( + if (size > (uint64_t) (uint32_t) -1) + return -EINVAL; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ + +@@ -3036,7 +3036,7 @@ static bool message_end_of_signature(sd_bus_message *m) { + + assert(m); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + return !c->signature || c->signature[c->index] == 0; + } + +@@ -3045,7 +3045,7 @@ static bool message_end_of_array(sd_bus_message *m, size_t index) { + + assert(m); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + if (c->enclosing != SD_BUS_TYPE_ARRAY) + return false; + +@@ -3306,7 +3306,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { + if (message_end_of_array(m, m->rindex)) + return 0; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + if (c->signature[c->index] != type) + return -ENXIO; + +@@ -4036,7 +4036,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m, + if (message_end_of_array(m, m->rindex)) + return 0; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + signature = strdup(contents); + if (!signature) +@@ -4092,7 +4092,7 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) { + assert_return(m->sealed, -EPERM); + assert_return(m->n_containers > 0, -ENXIO); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (c->enclosing != SD_BUS_TYPE_ARRAY) { + if (c->signature && c->signature[c->index] != 0) +@@ -4113,7 +4113,7 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) { + + message_free_last_container(m); + +- c = message_get_container(m); ++ c = message_get_last_container(m); + saved = c->index; + c->index = c->saved_index; + r = container_next_item(m, c, &m->rindex); +@@ -4132,7 +4132,7 @@ static void message_quit_container(sd_bus_message *m) { + assert(m->n_containers > 0); + + /* Undo seeks */ +- c = message_get_container(m); ++ c = message_get_last_container(m); + assert(m->rindex >= c->before); + m->rindex = c->before; + +@@ -4140,7 +4140,7 @@ static void message_quit_container(sd_bus_message *m) { + message_free_last_container(m); + + /* Correct index of new top-level container */ +- c = message_get_container(m); ++ c = message_get_last_container(m); + c->index = c->saved_index; + } + +@@ -4157,7 +4157,7 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + if (message_end_of_array(m, m->rindex)) + goto eof; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (bus_type_is_basic(c->signature[c->index])) { + if (contents) +@@ -4301,9 +4301,9 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) { + message_reset_containers(m); + m->rindex = 0; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + } else { +- c = message_get_container(m); ++ c = message_get_last_container(m); + + c->offset_index = 0; + c->index = 0; +@@ -4546,7 +4546,7 @@ _public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) { + if (message_end_of_array(m, m->rindex)) + return 0; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + r = signature_element_length(c->signature + c->index, &l); + if (r < 0) +@@ -4712,7 +4712,7 @@ _public_ int sd_bus_message_read_array( + if (r <= 0) + return r; + +- c = message_get_container(m); ++ c = message_get_last_container(m); + + if (BUS_MESSAGE_IS_GVARIANT(m)) { + align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); +@@ -5609,7 +5609,7 @@ _public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complet + + assert_return(m, NULL); + +- c = complete ? &m->root_container : message_get_container(m); ++ c = complete ? &m->root_container : message_get_last_container(m); + return strempty(c->signature); + } + diff --git a/SOURCES/0165-bus-message-use-define.patch b/SOURCES/0165-bus-message-use-define.patch new file mode 100644 index 0000000..0700407 --- /dev/null +++ b/SOURCES/0165-bus-message-use-define.patch @@ -0,0 +1,25 @@ +From 5d6a8b1b9728cfa54c89441a089ffbb156b59648 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 24 Jul 2018 21:24:53 +0200 +Subject: [PATCH] bus-message: use define + +(cherry picked from commit f22c308aff556bf5c6599ffcb61e637e366ab232) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 7392a43a19..81aaa7f59f 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -227,7 +227,7 @@ static int message_append_field_string( + /* dbus1 doesn't allow strings over 32bit, let's enforce this + * globally, to not risk convertability */ + l = strlen(s); +- if (l > (size_t) (uint32_t) -1) ++ if (l > UINT32_MAX) + return -EINVAL; + + /* Signature "(yv)" where the variant contains "s" */ diff --git a/SOURCES/0166-bus-do-not-print-null-if-the-message-has-unknown-typ.patch b/SOURCES/0166-bus-do-not-print-null-if-the-message-has-unknown-typ.patch new file mode 100644 index 0000000..1196d52 --- /dev/null +++ b/SOURCES/0166-bus-do-not-print-null-if-the-message-has-unknown-typ.patch @@ -0,0 +1,33 @@ +From 625795e6214ae7c7f0f7f35b2c1f53e1e173b1ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 2 Aug 2018 00:46:20 +0200 +Subject: [PATCH] bus: do not print (null) if the message has unknown type + +(cherry picked from commit e8fd7e4b5b5269377efc641a7da43850822c1250) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-dump.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c +index 3a28c7c6e3..2bd06053a6 100644 +--- a/src/libsystemd/sd-bus/bus-dump.c ++++ b/src/libsystemd/sd-bus/bus-dump.c +@@ -59,8 +59,14 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { + "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, + m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : + m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() : +- m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), +- ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), ++ m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", ++ special_glyph(TRIANGULAR_BULLET), ++ ansi_normal(), ++ ++ ansi_highlight(), ++ bus_message_type_to_string(m->header->type) ?: "(unknown)", ++ ansi_normal(), ++ + m->header->endian, + m->header->flags, + m->header->version, diff --git a/SOURCES/0167-bus-message-fix-calculation-of-offsets-table.patch b/SOURCES/0167-bus-message-fix-calculation-of-offsets-table.patch new file mode 100644 index 0000000..bb77b2b --- /dev/null +++ b/SOURCES/0167-bus-message-fix-calculation-of-offsets-table.patch @@ -0,0 +1,126 @@ +From 38a5ae776dc62b42ef5ced8f9812771181af528b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 2 Aug 2018 14:25:11 +0200 +Subject: [PATCH] bus-message: fix calculation of offsets table + +The offsets specify the ends of variable length data. We would trust the +incoming data, putting the offsets specified in our message +into the offsets tables after doing some superficial verification. +But when actually reading the data we apply alignment, so we would take +the previous offset, align it, making it bigger then current offset, and +then we'd try to read data of negative length. + +In the attached example, the message specifies the following offsets: +[1, 4] +but the alignment of those items is +[1, 8] +so we'd calculate the second item as starting at 8 and ending at 4. + +(cherry picked from commit 12603b84d2fb07603e2ea94b240c6b78ad17510e) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 36 +++++++++--------- + ...h-e1b811da5ca494e494b77c6bd8e1c2f2989425c5 | Bin 0 -> 28 bytes + 2 files changed, 18 insertions(+), 18 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5 + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 81aaa7f59f..d0af34f632 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -3140,6 +3140,7 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_ + assert(alignment > 0); + + *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); ++ assert(c->offsets[c->offset_index+1] >= *rindex); + c->item_size = c->offsets[c->offset_index+1] - *rindex; + } else { + +@@ -3179,6 +3180,7 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_ + assert(alignment > 0); + + *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); ++ assert(c->offsets[c->offset_index+1] >= *rindex); + c->item_size = c->offsets[c->offset_index+1] - *rindex; + + c->offset_index++; +@@ -3725,7 +3727,7 @@ static int build_struct_offsets( + size_t *n_offsets) { + + unsigned n_variable = 0, n_total = 0, v; +- size_t previous = 0, where; ++ size_t previous, where; + const char *p; + size_t sz; + void *q; +@@ -3804,6 +3806,7 @@ static int build_struct_offsets( + + /* Second, loop again and build an offset table */ + p = signature; ++ previous = m->rindex; + while (*p != 0) { + size_t n, offset; + int k; +@@ -3817,37 +3820,34 @@ static int build_struct_offsets( + memcpy(t, p, n); + t[n] = 0; + ++ size_t align = bus_gvariant_get_alignment(t); ++ assert(align > 0); ++ ++ /* The possible start of this member after including alignment */ ++ size_t start = ALIGN_TO(previous, align); ++ + k = bus_gvariant_get_size(t); + if (k < 0) { + size_t x; + +- /* variable size */ ++ /* Variable size */ + if (v > 0) { + v--; + + x = bus_gvariant_read_word_le((uint8_t*) q + v*sz, sz); + if (x >= size) + return -EBADMSG; +- if (m->rindex + x < previous) +- return -EBADMSG; + } else +- /* The last item's end +- * is determined from +- * the start of the +- * offset array */ ++ /* The last item's end is determined ++ * from the start of the offset array */ + x = size - (n_variable * sz); + + offset = m->rindex + x; +- +- } else { +- size_t align; +- +- /* fixed size */ +- align = bus_gvariant_get_alignment(t); +- assert(align > 0); +- +- offset = (*n_offsets == 0 ? m->rindex : ALIGN_TO((*offsets)[*n_offsets-1], align)) + k; +- } ++ if (offset < start) ++ return -EBADMSG; ++ } else ++ /* Fixed size */ ++ offset = start + k; + } + + previous = (*offsets)[(*n_offsets)++] = offset; +diff --git a/test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5 b/test/fuzz/fuzz-bus-message/crash-e1b811da5ca494e494b77c6bd8e1c2f2989425c5 +new file mode 100644 +index 0000000000000000000000000000000000000000..9d3fa0035fd360a37833e8b58cc4aea90df9de83 +GIT binary patch +literal 28 +fcmd1#|DTDG0Z1?a!8`>PAeqj{pplqVrYQgbfcytC + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0168-bus-message-remove-duplicate-assignment.patch b/SOURCES/0168-bus-message-remove-duplicate-assignment.patch new file mode 100644 index 0000000..000327d --- /dev/null +++ b/SOURCES/0168-bus-message-remove-duplicate-assignment.patch @@ -0,0 +1,24 @@ +From fcaaf6f3640c6cac73ba2b3807cde9fd94e0789b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 2 Aug 2018 14:25:31 +0200 +Subject: [PATCH] bus-message: remove duplicate assignment + +(cherry picked from commit 4d82a8d5052fce8c1ea51f8bdec3476fb8cc4747) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index d0af34f632..c8f7937102 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -4305,7 +4305,6 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) { + } else { + c = message_get_last_container(m); + +- c->offset_index = 0; + c->index = 0; + m->rindex = c->begin; + } diff --git a/SOURCES/0169-bus-message-fix-calculation-of-offsets-table-for-arr.patch b/SOURCES/0169-bus-message-fix-calculation-of-offsets-table-for-arr.patch new file mode 100644 index 0000000..c3ccf71 --- /dev/null +++ b/SOURCES/0169-bus-message-fix-calculation-of-offsets-table-for-arr.patch @@ -0,0 +1,87 @@ +From 871bb5457c5951870d447f53c976a1a1f2dac85d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 3 Aug 2018 14:46:57 +0200 +Subject: [PATCH] bus-message: fix calculation of offsets table for arrays + +This is similar to the grandparent commit 'fix calculation of offsets table', +except that now the change is for array elements. Same story as before: we need +to make sure that the offsets increase enough taking alignment into account. + +While at it, rename 'p' to 'previous' to match similar code in other places. + +(cherry picked from commit f88214cf9d66c93f4d22c4c8980de9ee3ff45bab) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 17 ++++++++++++----- + ...sh-d8f3941c74219b4c03532c9b244d5ea539c61af5 | Bin 0 -> 41 bytes + 2 files changed, 12 insertions(+), 5 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5 + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index c8f7937102..ac823aaf58 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -3532,7 +3532,7 @@ static int bus_message_enter_array( + + size_t rindex; + void *q; +- int r, alignment; ++ int r; + + assert(m); + assert(c); +@@ -3558,6 +3558,7 @@ static int bus_message_enter_array( + + if (!BUS_MESSAGE_IS_GVARIANT(m)) { + /* dbus1 */ ++ int alignment; + + r = message_peek_body(m, &rindex, 4, 4, &q); + if (r < 0) +@@ -3591,7 +3592,8 @@ static int bus_message_enter_array( + *n_offsets = 0; + + } else { +- size_t where, p = 0, framing, sz; ++ size_t where, previous = 0, framing, sz; ++ int alignment; + unsigned i; + + /* gvariant: variable length array */ +@@ -3619,17 +3621,22 @@ static int bus_message_enter_array( + if (!*offsets) + return -ENOMEM; + ++ alignment = bus_gvariant_get_alignment(c->signature); ++ assert(alignment > 0); ++ + for (i = 0; i < *n_offsets; i++) { +- size_t x; ++ size_t x, start; ++ ++ start = ALIGN_TO(previous, alignment); + + x = bus_gvariant_read_word_le((uint8_t*) q + i * sz, sz); + if (x > c->item_size - sz) + return -EBADMSG; +- if (x < p) ++ if (x < start) + return -EBADMSG; + + (*offsets)[i] = rindex + x; +- p = x; ++ previous = x; + } + + *item_size = (*offsets)[0] - rindex; +diff --git a/test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5 b/test/fuzz/fuzz-bus-message/crash-d8f3941c74219b4c03532c9b244d5ea539c61af5 +new file mode 100644 +index 0000000000000000000000000000000000000000..26262e1149825a114a89bf9cee5aeca0be463984 +GIT binary patch +literal 41 +rcmd1#|DTC5gMmSS0SHWtIT#p03 +Date: Fri, 3 Aug 2018 16:36:51 +0200 +Subject: [PATCH] bus-message: drop asserts in functions which are wrappers for + varargs version + +The function does no processing on it's own, and just forwards arguments +to the other function. Let's just use the asserts there. + +(cherry picked from commit 8792bdf8a3311f9e629daa0ec592c97c1cfb2a7c) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index ac823aaf58..153cdf933b 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -2469,11 +2469,6 @@ _public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) { + va_list ap; + int r; + +- assert_return(m, -EINVAL); +- assert_return(types, -EINVAL); +- assert_return(!m->sealed, -EPERM); +- assert_return(!m->poisoned, -ESTALE); +- + va_start(ap, types); + r = sd_bus_message_appendv(m, types, ap); + va_end(ap); +@@ -4524,10 +4519,6 @@ _public_ int sd_bus_message_read(sd_bus_message *m, const char *types, ...) { + va_list ap; + int r; + +- assert_return(m, -EINVAL); +- assert_return(m->sealed, -EPERM); +- assert_return(types, -EINVAL); +- + va_start(ap, types); + r = message_read_ap(m, types, ap); + va_end(ap); diff --git a/SOURCES/0171-bus-message-output-debug-information-about-offset-tr.patch b/SOURCES/0171-bus-message-output-debug-information-about-offset-tr.patch new file mode 100644 index 0000000..615a7f0 --- /dev/null +++ b/SOURCES/0171-bus-message-output-debug-information-about-offset-tr.patch @@ -0,0 +1,29 @@ +From f6af2bfe4b353b25a61c362c3ada9be06c8f15c9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 3 Aug 2018 18:05:27 +0200 +Subject: [PATCH] bus-message: output debug information about offset troubles + +(cherry picked from commit 0b4775b52747bebf7ecb62062798475629767044) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 153cdf933b..09e72d89dd 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -3845,8 +3845,11 @@ static int build_struct_offsets( + x = size - (n_variable * sz); + + offset = m->rindex + x; +- if (offset < start) ++ if (offset < start) { ++ log_debug("For type %s with alignment %zu, message specifies offset %zu which is smaller than previous end %zu + alignment = %zu", ++ t, align, offset, previous, start); + return -EBADMSG; ++ } + } else + /* Fixed size */ + offset = start + k; diff --git a/SOURCES/0172-bus-message-fix-skipping-of-array-fields-in-gvariant.patch b/SOURCES/0172-bus-message-fix-skipping-of-array-fields-in-gvariant.patch new file mode 100644 index 0000000..5f7774b --- /dev/null +++ b/SOURCES/0172-bus-message-fix-skipping-of-array-fields-in-gvariant.patch @@ -0,0 +1,59 @@ +From d212765dc94ba25c04e0e9a278586f0e86851e35 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 11 Aug 2018 08:32:20 +0200 +Subject: [PATCH] bus-message: fix skipping of array fields in !gvariant + messages + +We copied part of the string into a buffer that was off by two. +If the element signature had length one, we'd copy 0 bytes and crash when +looking at the "first" byte. Otherwise, we would crash because strncpy would +not terminate the string. + +(cherry picked from commit 73777ddba5100fe6c0791cd37a91f24a515f3202) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 8 ++++---- + ...crash-37449529b1ad867f0c2671fa80aca5d7812a2b70 | Bin 0 -> 534 bytes + 2 files changed, 4 insertions(+), 4 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70 + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 09e72d89dd..202f1aab30 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -4981,18 +4981,18 @@ static int message_skip_fields( + + } else if (t == SD_BUS_TYPE_ARRAY) { + +- r = signature_element_length(*signature+1, &l); ++ r = signature_element_length(*signature + 1, &l); + if (r < 0) + return r; + + assert(l >= 1); + { +- char sig[l-1], *s; ++ char sig[l + 1], *s = sig; + uint32_t nas; + int alignment; + +- strncpy(sig, *signature + 1, l-1); +- s = sig; ++ strncpy(sig, *signature + 1, l); ++ sig[l] = '\0'; + + alignment = bus_type_get_alignment(sig[0]); + if (alignment < 0) +diff --git a/test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70 b/test/fuzz/fuzz-bus-message/crash-37449529b1ad867f0c2671fa80aca5d7812a2b70 +new file mode 100644 +index 0000000000000000000000000000000000000000..6a20265a39e1b4a318b50aee2b13727ddc4113bf +GIT binary patch +literal 534 +zcmc~{WMHggWMD`aVqj=xU|>*W&P&W-;Q0Fg|9>Elfq|V9OfmRED27Bi2!jjC2Wn-| +z17hYPAOVtNW-Ml42GVKy`9P9^ffdMS1=8h-IVt%J91NTwNgyEFV4&K>#6$*=MMgl( +r%#fH?l1eMv=;=K=_yi-CK!KUB2_%6r0c0u^mlS2@rGxk|0FGY(dwVLU + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0173-bus-message-also-properly-copy-struct-signature-when.patch b/SOURCES/0173-bus-message-also-properly-copy-struct-signature-when.patch new file mode 100644 index 0000000..89e0100 --- /dev/null +++ b/SOURCES/0173-bus-message-also-properly-copy-struct-signature-when.patch @@ -0,0 +1,33 @@ +From 13993b51c5cab806d81a7305c895bafd4cd48876 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 11 Aug 2018 09:02:48 +0200 +Subject: [PATCH] bus-message: also properly copy struct signature when + skipping + +The change is similar to that in the previous commit, but I don't have +a reproducer / test case case for this one, so I'm keeping it seperate. + +(cherry picked from commit 3d338a302f56c0ef0445660d9856794abe1af8b5) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 202f1aab30..e71d29f91d 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -5036,9 +5036,9 @@ static int message_skip_fields( + + assert(l >= 2); + { +- char sig[l-1], *s; +- strncpy(sig, *signature + 1, l-1); +- s = sig; ++ char sig[l + 1], *s = sig; ++ strncpy(sig, *signature + 1, l); ++ sig[l] = '\0'; + + r = message_skip_fields(m, ri, (uint32_t) -1, (const char**) &s); + if (r < 0) diff --git a/SOURCES/0174-fuzz-bus-message-add-two-test-cases-that-pass-now.patch b/SOURCES/0174-fuzz-bus-message-add-two-test-cases-that-pass-now.patch new file mode 100644 index 0000000..cd69663 --- /dev/null +++ b/SOURCES/0174-fuzz-bus-message-add-two-test-cases-that-pass-now.patch @@ -0,0 +1,39 @@ +From 5ae60bf0848d38b101f8c79ffa82efcb27d6767c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 11 Aug 2018 11:31:45 +0200 +Subject: [PATCH] fuzz-bus-message: add two test cases that pass now + +It seems that they got fixed by one of the patches. Let's add them +just in case. + +(cherry picked from commit edde66ffc2404de58e8b19810951f376efb344da) + +Resolves: #1696224 +--- + ...crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45 | Bin 0 -> 89 bytes + ...crash-4f0211eb269e28db941961061494bfdbf3345e54 | Bin 0 -> 143 bytes + 2 files changed, 0 insertions(+), 0 deletions(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45 + create mode 100644 test/fuzz/fuzz-bus-message/crash-4f0211eb269e28db941961061494bfdbf3345e54 + +diff --git a/test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45 b/test/fuzz/fuzz-bus-message/crash-32bf69483cbd4f2e6d46c25a2f92a472109aee45 +new file mode 100644 +index 0000000000000000000000000000000000000000..aa0c6ff7f7b6d2e3fa4358716ee1d05ba74cefc0 +GIT binary patch +literal 89 +scmc~q)(CT7OTMiz#q2KEMtDT&4= +liD{N8W@+Y0hK!61V4zWvq@j_NlvJFQnL&wm%}Py80RUy_C0PIf + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0175-bus-message-return-EBADMSG-not-EINVAL-on-invalid-gva.patch b/SOURCES/0175-bus-message-return-EBADMSG-not-EINVAL-on-invalid-gva.patch new file mode 100644 index 0000000..0cd8d72 --- /dev/null +++ b/SOURCES/0175-bus-message-return-EBADMSG-not-EINVAL-on-invalid-gva.patch @@ -0,0 +1,41 @@ +From b63440ad69581bad39a2eda7ab449f8a3f901c4e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 11 Aug 2018 11:43:09 +0200 +Subject: [PATCH] bus-message: return -EBADMSG not -EINVAL on invalid !gvariant + messages + +(cherry picked from commit d831fb6f2bde829f9309aea242f502587662d1cc) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 2 +- + ...crash-4162a61a79e4c5a832ca5232212f75fa560a1f75 | Bin 0 -> 534 bytes + 2 files changed, 1 insertion(+), 1 deletion(-) + create mode 100644 test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75 + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index e71d29f91d..613722a1a0 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -5047,7 +5047,7 @@ static int message_skip_fields( + + *signature += l; + } else +- return -EINVAL; ++ return -EBADMSG; + } + } + +diff --git a/test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75 b/test/fuzz/fuzz-bus-message/crash-4162a61a79e4c5a832ca5232212f75fa560a1f75 +new file mode 100644 +index 0000000000000000000000000000000000000000..5faf3308e7ac9c14d66422169e74ba8c05ad7319 +GIT binary patch +literal 534 +zcmd5(y$ZrW3{L#Rf|Cy*1sA)t;uE+zxcCZJw53qIqj#v2xH$UGez{(yI63-3NWO$5 +zU+!uqzB5rdCwdYQvnEi=V1glA8o?i`lMy}upTQSe=c-Assy=GTr+lHv=4$0!Vy$EX +z_LzYX&1*Ob(W(=vPGKsxuBpzYaDn6&un5*x;uk`Xz?Yk^O%qgGJ(zd +Date: Thu, 23 Aug 2018 14:48:40 +0200 +Subject: [PATCH] bus-message: avoid wrap-around when using length read from + message + +We would read (-1), and then add 1 to it, call message_peek_body(..., 0, ...), +and when trying to make use of the data. + +The fuzzer test case is just for one site, but they all look similar. + +v2: fix two UINT8_MAX/UINT32_MAX mismatches founds by LGTM +(cherry picked from commit 902000c19830f5e5a96e8948d691b42e91ecb1e7) + +Resolves: #1696224 +--- + src/libsystemd/sd-bus/bus-message.c | 24 ++++++++++++++++++ + ...h-603dfd98252375ac7dbced53c2ec312671939a36 | Bin 0 -> 40 bytes + 2 files changed, 24 insertions(+) + create mode 100644 test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36 + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 613722a1a0..53cbd675b7 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -3414,6 +3414,10 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { + return r; + + l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q); ++ if (l == UINT32_MAX) ++ /* avoid overflow right below */ ++ return -EBADMSG; ++ + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; +@@ -3436,6 +3440,10 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) { + return r; + + l = *(uint8_t*) q; ++ if (l == UINT8_MAX) ++ /* avoid overflow right below */ ++ return -EBADMSG; ++ + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; +@@ -3701,6 +3709,10 @@ static int bus_message_enter_variant( + return r; + + l = *(uint8_t*) q; ++ if (l == UINT8_MAX) ++ /* avoid overflow right below */ ++ return -EBADMSG; ++ + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; +@@ -4269,6 +4281,10 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char + return r; + + l = *(uint8_t*) q; ++ if (l == UINT8_MAX) ++ /* avoid overflow right below */ ++ return -EBADMSG; ++ + r = message_peek_body(m, &rindex, 1, l+1, &q); + if (r < 0) + return r; +@@ -4849,6 +4865,10 @@ static int message_peek_field_string( + if (r < 0) + return r; + ++ if (l == UINT32_MAX) ++ /* avoid overflow right below */ ++ return -EBADMSG; ++ + r = message_peek_fields(m, ri, 1, l+1, &q); + if (r < 0) + return r; +@@ -4900,6 +4920,10 @@ static int message_peek_field_signature( + return r; + + l = *(uint8_t*) q; ++ if (l == UINT8_MAX) ++ /* avoid overflow right below */ ++ return -EBADMSG; ++ + r = message_peek_fields(m, ri, 1, l+1, &q); + if (r < 0) + return r; +diff --git a/test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36 b/test/fuzz/fuzz-bus-message/crash-603dfd98252375ac7dbced53c2ec312671939a36 +new file mode 100644 +index 0000000000000000000000000000000000000000..b3fee9e07af4f925697a549bbc8ffc03a277fac0 +GIT binary patch +literal 40 +mcmc~{Vqjzdg7laF|BC@>cE)0c{}2$`*K@IKT2AZ~5ElR}@e}O; + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0177-util-do-not-use-stack-frame-for-parsing-arbitrary-in.patch b/SOURCES/0177-util-do-not-use-stack-frame-for-parsing-arbitrary-in.patch new file mode 100644 index 0000000..296afc3 --- /dev/null +++ b/SOURCES/0177-util-do-not-use-stack-frame-for-parsing-arbitrary-in.patch @@ -0,0 +1,66 @@ +From a652268ae11633cf64c87586bed1fd3c7141707a Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 22 Aug 2018 12:33:27 +0900 +Subject: [PATCH] util: do not use stack frame for parsing arbitrary inputs + +This replaces strndupa() by strndup() in socket_address_parse(), +as input string may be too long. + +Fixes issue 10007 by ClusterFuzz-External: +https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=10007 + +(cherry picked from commit 8d30fcb9b51b1d102a589171b6e28f5f370236f6) + +Resolves: #1696224 +--- + src/basic/socket-util.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c +index a913102e13..3f90a81d35 100644 +--- a/src/basic/socket-util.c ++++ b/src/basic/socket-util.c +@@ -50,7 +50,8 @@ static const char* const socket_address_type_table[] = { + DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); + + int socket_address_parse(SocketAddress *a, const char *s) { +- char *e, *n; ++ _cleanup_free_ char *n = NULL; ++ char *e; + int r; + + assert(a); +@@ -68,7 +69,9 @@ int socket_address_parse(SocketAddress *a, const char *s) { + if (!e) + return -EINVAL; + +- n = strndupa(s+1, e-s-1); ++ n = strndup(s+1, e-s-1); ++ if (!n) ++ return -ENOMEM; + + errno = 0; + if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) +@@ -125,7 +128,10 @@ int socket_address_parse(SocketAddress *a, const char *s) { + if (r < 0) + return r; + +- n = strndupa(cid_start, e - cid_start); ++ n = strndup(cid_start, e - cid_start); ++ if (!n) ++ return -ENOMEM; ++ + if (!isempty(n)) { + r = safe_atou(n, &a->sockaddr.vm.svm_cid); + if (r < 0) +@@ -146,7 +152,9 @@ int socket_address_parse(SocketAddress *a, const char *s) { + if (r < 0) + return r; + +- n = strndupa(s, e-s); ++ n = strndup(s, e-s); ++ if (!n) ++ return -ENOMEM; + + /* IPv4 in w.x.y.z:p notation? */ + r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); diff --git a/SOURCES/0178-travis-enable-ASan-and-UBSan-on-RHEL8.patch b/SOURCES/0178-travis-enable-ASan-and-UBSan-on-RHEL8.patch new file mode 100644 index 0000000..633d215 --- /dev/null +++ b/SOURCES/0178-travis-enable-ASan-and-UBSan-on-RHEL8.patch @@ -0,0 +1,213 @@ +From d84b1c62b9739e9c043a717aecec2da181eb9df7 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Sat, 23 Feb 2019 17:10:55 +0100 +Subject: [PATCH] travis: enable ASan and UBSan on RHEL8 + +Resolves: #1683319 +rhel-only +--- + .travis.yml | 23 ++++++- + ci/travis-centos-rhel8.sh | 138 +++++++++++++++++++++++--------------- + 2 files changed, 105 insertions(+), 56 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index c5c9c345a9..67677bdf06 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -8,8 +8,7 @@ env: + + jobs: + include: +- - stage: Build & test +- name: CentOS 7 ++ - name: CentOS 7 + language: bash + env: + - CENTOS_RELEASE="centos7" +@@ -28,3 +27,23 @@ jobs: + - set +e + after_script: + - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh CLEANUP ++ ++ - name: CentOS 7 (ASan+UBSan) ++ language: bash ++ env: ++ - CENTOS_RELEASE="centos7" ++ - CONT_NAME="systemd-centos-$CENTOS_RELEASE" ++ - DOCKER_EXEC="docker exec -ti $CONT_NAME" ++ before_install: ++ - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce ++ - docker --version ++ install: ++ - if [ -f meson.build ]; then RHEL_VERSION=rhel8; else RHEL_VERSION=rhel7; fi ++ - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh SETUP ++ script: ++ - set -e ++ # Build systemd ++ - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh RUN_ASAN ++ - set +e ++ after_script: ++ - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh CLEANUP +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index 1f72d984e0..c3d1018682 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -19,6 +19,60 @@ ADDITIONAL_DEPS=(systemd-ci-environment libidn2-devel python-lxml python36 ninja + # Repo with additional depencencies to compile newer systemd on CentOS 7 + COPR_REPO="https://copr.fedorainfracloud.org/coprs/mrc0mmand/systemd-centos-ci/repo/epel-7/mrc0mmand-systemd-centos-ci-epel-7.repo" + COPR_REPO_PATH="/etc/yum.repos.d/${COPR_REPO##*/}" ++# RHEL8 options ++CONFIGURE_OPTS=( ++ -Dsysvinit-path=/etc/rc.d/init.d ++ -Drc-local=/etc/rc.d/rc.local ++ -Ddns-servers='' ++ -Ddev-kvm-mode=0666 ++ -Dkmod=true ++ -Dxkbcommon=true ++ -Dblkid=true ++ -Dseccomp=true ++ -Dima=true ++ -Dselinux=true ++ -Dapparmor=false ++ -Dpolkit=true ++ -Dxz=true ++ -Dzlib=true ++ -Dbzip2=true ++ -Dlz4=true ++ -Dpam=true ++ -Dacl=true ++ -Dsmack=true ++ -Dgcrypt=true ++ -Daudit=true ++ -Delfutils=true ++ -Dlibcryptsetup=true ++ -Delfutils=true ++ -Dqrencode=false ++ -Dgnutls=true ++ -Dmicrohttpd=true ++ -Dlibidn2=true ++ -Dlibiptc=true ++ -Dlibcurl=true ++ -Defi=true ++ -Dtpm=true ++ -Dhwdb=true ++ -Dsysusers=true ++ -Ddefault-kill-user-processes=false ++ -Dtests=unsafe ++ -Dinstall-tests=true ++ -Dtty-gid=5 ++ -Dusers-gid=100 ++ -Dnobody-user=nobody ++ -Dnobody-group=nobody ++ -Dsplit-usr=false ++ -Dsplit-bin=true ++ -Db_lto=false ++ -Dnetworkd=false ++ -Dtimesyncd=false ++ -Ddefault-hierarchy=legacy ++ # Custom options ++ -Dslow-tests=true ++ -Dtests=unsafe ++ -Dinstall-tests=true ++) + + function info() { + echo -e "\033[33;1m$1\033[0m" +@@ -57,60 +111,6 @@ for phase in "${PHASES[@]}"; do + RUN) + info "Run phase" + # Build systemd +- CONFIGURE_OPTS=( +- # RHEL8 options +- -Dsysvinit-path=/etc/rc.d/init.d +- -Drc-local=/etc/rc.d/rc.local +- -Ddns-servers='' +- -Ddev-kvm-mode=0666 +- -Dkmod=true +- -Dxkbcommon=true +- -Dblkid=true +- -Dseccomp=true +- -Dima=true +- -Dselinux=true +- -Dapparmor=false +- -Dpolkit=true +- -Dxz=true +- -Dzlib=true +- -Dbzip2=true +- -Dlz4=true +- -Dpam=true +- -Dacl=true +- -Dsmack=true +- -Dgcrypt=true +- -Daudit=true +- -Delfutils=true +- -Dlibcryptsetup=true +- -Delfutils=true +- -Dqrencode=false +- -Dgnutls=true +- -Dmicrohttpd=true +- -Dlibidn2=true +- -Dlibiptc=true +- -Dlibcurl=true +- -Defi=true +- -Dtpm=true +- -Dhwdb=true +- -Dsysusers=true +- -Ddefault-kill-user-processes=false +- -Dtests=unsafe +- -Dinstall-tests=true +- -Dtty-gid=5 +- -Dusers-gid=100 +- -Dnobody-user=nobody +- -Dnobody-group=nobody +- -Dsplit-usr=false +- -Dsplit-bin=true +- -Db_lto=false +- -Dnetworkd=false +- -Dtimesyncd=false +- -Ddefault-hierarchy=legacy +- # Custom options +- -Dslow-tests=true +- -Dtests=unsafe +- -Dinstall-tests=true +- ) + docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build "${CONFIGURE_OPTS[@]}" + $DOCKER_EXEC ninja -v -C build + # Let's install the new systemd and "reboot" the container to avoid +@@ -122,6 +122,36 @@ for phase in "${PHASES[@]}"; do + echo -ne "#!/usr/bin/perl\nexit(0);\n" > "test/udev-test.pl" + $DOCKER_EXEC ninja -C build test + ;; ++ RUN_ASAN|RUN_CLANG_ASAN) ++ # Let's install newer gcc for proper ASan/UBSan support ++ $DOCKER_EXEC yum -y install centos-release-scl ++ $DOCKER_EXEC yum -y install devtoolset-8 devtoolset-8-libasan-devel libasan5 devtoolset-8-libubsan-devel libubsan1 ++ $DOCKER_EXEC bash -c "echo 'source scl_source enable devtoolset-8' >> /root/.bashrc" ++ # Note to my future frustrated self: docker exec runs the given command ++ # as sh -c 'command' - which means both .bash_profile and .bashrc will ++ # be ignored. That's because .bash_profile is sourced for LOGIN shells (i.e. ++ # sh -l), whereas .bashrc is sourced for NON-LOGIN INTERACTIVE shells ++ # (i.e. sh -i). ++ # As the default docker exec command lacks either of those options, ++ # we need to use a wrapper command which runs the wanted command ++ # under an explicit bash -i, so the SCL source above works properly. ++ docker exec -it $CONT_NAME bash -ic 'gcc --version' ++ ++ if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then ++ ENV_VARS="-e CC=clang -e CXX=clang++" ++ MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764 ++ fi ++ docker exec $ENV_VARS -it $CONT_NAME bash -ic "meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS ${CONFIGURE_OPTS[@]}" ++ docker exec -it $CONT_NAME bash -ic 'ninja -v -C build' ++ ++ # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb. ++ travis_wait docker exec --interactive=false \ ++ -e UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \ ++ -e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \ ++ -e "TRAVIS=$TRAVIS" \ ++ -t $CONT_NAME \ ++ bash -ic 'meson test --timeout-multiplier=3 -C ./build/ --print-errorlogs' ++ ;; + CLEANUP) + info "Cleanup phase" + docker stop $CONT_NAME diff --git a/SOURCES/0179-tests-keep-SYS_PTRACE-when-running-under-ASan.patch b/SOURCES/0179-tests-keep-SYS_PTRACE-when-running-under-ASan.patch new file mode 100644 index 0000000..2a509c5 --- /dev/null +++ b/SOURCES/0179-tests-keep-SYS_PTRACE-when-running-under-ASan.patch @@ -0,0 +1,31 @@ +From ee93272cf9915710251704c7bf7c1f1a0b02cb7f Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 9 Nov 2018 12:47:30 +0100 +Subject: [PATCH] tests: keep SYS_PTRACE when running under ASan + +(cherry picked from commit 7a3025658836c536f81fdd742fa338545294f5bf) + +Resolves: #1683319 +--- + src/test/test-capability.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/test/test-capability.c b/src/test/test-capability.c +index 72975cef94..20eb877a3c 100644 +--- a/src/test/test-capability.c ++++ b/src/test/test-capability.c +@@ -19,8 +19,13 @@ + static uid_t test_uid = -1; + static gid_t test_gid = -1; + ++#ifdef __SANITIZE_ADDRESS__ ++/* Keep CAP_SYS_PTRACE when running under Address Sanitizer */ ++static const uint64_t test_flags = UINT64_C(1) << CAP_SYS_PTRACE; ++#else + /* We keep CAP_DAC_OVERRIDE to avoid errors with gcov when doing test coverage */ +-static uint64_t test_flags = 1ULL << CAP_DAC_OVERRIDE; ++static const uint64_t test_flags = UINT64_C(1) << CAP_DAC_OVERRIDE; ++#endif + + /* verify cap_last_cap() against /proc/sys/kernel/cap_last_cap */ + static void test_last_cap_file(void) { diff --git a/SOURCES/0180-tree-wide-various-ubsan-zero-size-memory-fixes.patch b/SOURCES/0180-tree-wide-various-ubsan-zero-size-memory-fixes.patch new file mode 100644 index 0000000..05617f9 --- /dev/null +++ b/SOURCES/0180-tree-wide-various-ubsan-zero-size-memory-fixes.patch @@ -0,0 +1,60 @@ +From 6bb1d52f0554f687ef27de46c0b9daac9d256d60 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 10 Oct 2018 11:34:30 +0200 +Subject: [PATCH] tree-wide: various ubsan zero size memory fixes + +Fixes: #10346 +(cherry picked from commit 65f95765d05ddcd9e5849b68c379afa7e87d1248) + +Resolves: #1683319 +--- + src/basic/bitmap.c | 2 +- + src/basic/util.h | 8 +++++++- + src/test/test-hexdecoct.c | 2 +- + 3 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c +index c17c6a7a02..a4cd6451b0 100644 +--- a/src/basic/bitmap.c ++++ b/src/basic/bitmap.c +@@ -206,7 +206,7 @@ bool bitmap_equal(Bitmap *a, Bitmap *b) { + return true; + + common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps); +- if (memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0) ++ if (memcmp_safe(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0) + return false; + + c = a->n_bitmaps > b->n_bitmaps ? a : b; +diff --git a/src/basic/util.h b/src/basic/util.h +index b68ef25ed8..4659a21b06 100644 +--- a/src/basic/util.h ++++ b/src/basic/util.h +@@ -134,7 +134,13 @@ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { + + int on_ac_power(void); + +-#define memzero(x,l) (memset((x), 0, (l))) ++#define memzero(x,l) \ ++ ({ \ ++ size_t _l_ = (l); \ ++ void *_x_ = (x); \ ++ _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \ ++ }) ++ + #define zero(x) (memzero(&(x), sizeof(x))) + + static inline void *mempset(void *s, int c, size_t n) { +diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c +index da9f3008bb..a972ddcef7 100644 +--- a/src/test/test-hexdecoct.c ++++ b/src/test/test-hexdecoct.c +@@ -84,7 +84,7 @@ static void test_unhexmem_one(const char *s, size_t l, int retval) { + l = strlen(s); + + assert_se(hex = hexmem(mem, len)); +- answer = strndupa(s, l); ++ answer = strndupa(s ?: "", l); + assert_se(streq(delete_chars(answer, WHITESPACE), hex)); + } + } diff --git a/SOURCES/0181-util-introduce-memcmp_safe.patch b/SOURCES/0181-util-introduce-memcmp_safe.patch new file mode 100644 index 0000000..c56f5ff --- /dev/null +++ b/SOURCES/0181-util-introduce-memcmp_safe.patch @@ -0,0 +1,27 @@ +From 465534a9417b7d7cf74f686da674b3f74d77ac58 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 8 Aug 2018 16:22:55 +0900 +Subject: [PATCH] util: introduce memcmp_safe() + +(cherry picked from commit f30faf854b9bf01da294547a1bc3660506d750db) + +Resolves: #1683319 +--- + src/basic/util.h | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/basic/util.h b/src/basic/util.h +index 4659a21b06..c70467f98c 100644 +--- a/src/basic/util.h ++++ b/src/basic/util.h +@@ -113,9 +113,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, int (*com + qsort_r(base, nmemb, size, compar, userdata); + } + +-/** +- * Normal memcpy requires src to be nonnull. We do nothing if n is 0. +- */ ++/* 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; diff --git a/SOURCES/0182-test-socket-util-avoid-memleak-reported-by-valgrind.patch b/SOURCES/0182-test-socket-util-avoid-memleak-reported-by-valgrind.patch new file mode 100644 index 0000000..085b163 --- /dev/null +++ b/SOURCES/0182-test-socket-util-avoid-memleak-reported-by-valgrind.patch @@ -0,0 +1,49 @@ +From fe6895fb6a5f8c61f0c47aa95e1c86bb88b7cf4f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 21 Aug 2018 19:44:48 +0200 +Subject: [PATCH] test-socket-util: avoid "memleak" reported by valgrind + +valgrind reports the allocation done in the short-lived child as a leak. +Let's restructure the code to avoid this. + +(cherry picked from commit 181c4ba750770b54a54b5abbe8ae8ff4f6db59b5) + +Resolves: #1683319 +--- + src/test/test-socket-util.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c +index ac2ea52a5c..588485d881 100644 +--- a/src/test/test-socket-util.c ++++ b/src/test/test-socket-util.c +@@ -431,7 +431,6 @@ static void test_getpeercred_getpeergroups(void) { + if (r == 0) { + static const gid_t gids[] = { 3, 4, 5, 6, 7 }; + gid_t *test_gids; +- _cleanup_free_ gid_t *peer_groups = NULL; + size_t n_test_gids; + uid_t test_uid; + gid_t test_gid; +@@ -472,12 +471,16 @@ static void test_getpeercred_getpeergroups(void) { + assert_se(ucred.gid == test_gid); + assert_se(ucred.pid == getpid_cached()); + +- r = getpeergroups(pair[0], &peer_groups); +- assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)); ++ { ++ _cleanup_free_ gid_t *peer_groups = NULL; + +- if (r >= 0) { +- assert_se((size_t) r == n_test_gids); +- assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0); ++ r = getpeergroups(pair[0], &peer_groups); ++ assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)); ++ ++ if (r >= 0) { ++ assert_se((size_t) r == n_test_gids); ++ assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0); ++ } + } + + safe_close_pair(pair); diff --git a/SOURCES/0183-sd-journal-escape-binary-data-in-match_make_string.patch b/SOURCES/0183-sd-journal-escape-binary-data-in-match_make_string.patch new file mode 100644 index 0000000..a4fd0f9 --- /dev/null +++ b/SOURCES/0183-sd-journal-escape-binary-data-in-match_make_string.patch @@ -0,0 +1,57 @@ +From b234013c618e3a346fd831d65e08662844fc9f81 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 12 Oct 2018 12:17:04 +0000 +Subject: [PATCH] sd-journal: escape binary data in match_make_string() + +Fixes: #10383 +(cherry picked from commit 9e8b1ec08e8eb0b4611b7caf6adb8828feb32312) + +Resolves: #1683319 +--- + src/journal/sd-journal.c | 3 ++- + src/journal/test-journal-match.c | 4 +++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c +index 83abd82d1c..323300baec 100644 +--- a/src/journal/sd-journal.c ++++ b/src/journal/sd-journal.c +@@ -16,6 +16,7 @@ + #include "catalog.h" + #include "compress.h" + #include "dirent-util.h" ++#include "escape.h" + #include "fd-util.h" + #include "fileio.h" + #include "format-util.h" +@@ -381,7 +382,7 @@ static char *match_make_string(Match *m) { + return strdup("none"); + + if (m->type == MATCH_DISCRETE) +- return strndup(m->data, m->size); ++ return cescape_length(m->data, m->size); + + LIST_FOREACH(matches, i, m->matches) { + char *t, *k; +diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c +index 4e5ad1791a..d2a52b9145 100644 +--- a/src/journal/test-journal-match.c ++++ b/src/journal/test-journal-match.c +@@ -23,6 +23,8 @@ int main(int argc, char *argv[]) { + assert_se(sd_journal_add_match(j, "", 0) < 0); + assert_se(sd_journal_add_match(j, "=", 0) < 0); + assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0); ++ assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0); ++ assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0); + assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); +@@ -53,7 +55,7 @@ int main(int argc, char *argv[]) { + + printf("resulting match expression is: %s\n", t); + +- assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))")); ++ assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO) AND B=C\\000D AND A=\\001\\002)))")); + + return 0; + } diff --git a/SOURCES/0184-capability-introduce-CAP_TO_MASK_CORRECTED-macro-rep.patch b/SOURCES/0184-capability-introduce-CAP_TO_MASK_CORRECTED-macro-rep.patch new file mode 100644 index 0000000..2141b6a --- /dev/null +++ b/SOURCES/0184-capability-introduce-CAP_TO_MASK_CORRECTED-macro-rep.patch @@ -0,0 +1,46 @@ +From 401f1fdc309175d3920c0fe168e52c601474c000 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 10 Oct 2018 11:07:54 +0200 +Subject: [PATCH] capability: introduce CAP_TO_MASK_CORRECTED() macro replacing + CAP_TO_MASK() + +linux/capability.h's CAP_TO_MASK potentially shifts a signed int "1" +(i.e. 32bit wide) left by 31 which means it becomes negative. That's +just weird, and ubsan complains about it. Let's introduce our own macro +CAP_TO_MASK_CORRECTED which doesn't fall into this trap, and make use of +it. + +Fixes: #10347 +(cherry picked from commit 5f00c5684f96c93a22840f7241ee444b9a632b1e) + +Resolves: #1683319 +--- + src/basic/capability-util.h | 4 ++++ + src/libsystemd/sd-bus/bus-creds.c | 2 +- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h +index 4a4a86093a..59591d4b52 100644 +--- a/src/basic/capability-util.h ++++ b/src/basic/capability-util.h +@@ -39,3 +39,7 @@ static inline bool cap_test_all(uint64_t caps) { + } + + bool ambient_capabilities_supported(void); ++ ++/* Identical to linux/capability.h's CAP_TO_MASK(), but uses an unsigned 1U instead of a signed 1 for shifting left, in ++ * order to avoid complaints about shifting a signed int left by 31 bits, which would make it negative. */ ++#define CAP_TO_MASK_CORRECTED(x) (1U << ((x) & 31U)) +diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c +index aae9fcd58b..b180a033b8 100644 +--- a/src/libsystemd/sd-bus/bus-creds.c ++++ b/src/libsystemd/sd-bus/bus-creds.c +@@ -663,7 +663,7 @@ static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { + + sz = DIV_ROUND_UP(cap_last_cap(), 32U); + +- return !!(c->capability[offset * sz + CAP_TO_INDEX(capability)] & CAP_TO_MASK(capability)); ++ return !!(c->capability[offset * sz + CAP_TO_INDEX((uint32_t) capability)] & CAP_TO_MASK_CORRECTED((uint32_t) capability)); + } + + _public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) { diff --git a/SOURCES/0185-sd-bus-use-size_t-when-dealing-with-memory-offsets.patch b/SOURCES/0185-sd-bus-use-size_t-when-dealing-with-memory-offsets.patch new file mode 100644 index 0000000..5c9c680 --- /dev/null +++ b/SOURCES/0185-sd-bus-use-size_t-when-dealing-with-memory-offsets.patch @@ -0,0 +1,25 @@ +From 51747496a38894d76d3e5b4295c54b1654b7eb69 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 10 Oct 2018 11:12:22 +0200 +Subject: [PATCH] sd-bus: use size_t when dealing with memory offsets + +(cherry picked from commit 3cae6c21e732fd46ff024d6625243d88ef6377ed) + +Resolves: #1683319 +--- + src/libsystemd/sd-bus/bus-creds.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c +index b180a033b8..6896bcf246 100644 +--- a/src/libsystemd/sd-bus/bus-creds.c ++++ b/src/libsystemd/sd-bus/bus-creds.c +@@ -651,7 +651,7 @@ _public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { + return 0; + } + +-static int has_cap(sd_bus_creds *c, unsigned offset, int capability) { ++static int has_cap(sd_bus_creds *c, size_t offset, int capability) { + size_t sz; + + assert(c); diff --git a/SOURCES/0186-sd-bus-call-cap_last_cap-only-once-in-has_cap.patch b/SOURCES/0186-sd-bus-call-cap_last_cap-only-once-in-has_cap.patch new file mode 100644 index 0000000..8cb9689 --- /dev/null +++ b/SOURCES/0186-sd-bus-call-cap_last_cap-only-once-in-has_cap.patch @@ -0,0 +1,40 @@ +From 770b16b22dd0eec04fd1493dae644206c32f8e04 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 10 Oct 2018 11:12:54 +0200 +Subject: [PATCH] sd-bus: call cap_last_cap() only once in has_cap() + +Also, use the same type everywhere for dealing with it. + +(cherry picked from commit 92a40e20bf970c3ded8a50fbeeae882a7b970c9a) + +Resolves: #1683319 +--- + src/libsystemd/sd-bus/bus-creds.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c +index 6896bcf246..2a47c68ffc 100644 +--- a/src/libsystemd/sd-bus/bus-creds.c ++++ b/src/libsystemd/sd-bus/bus-creds.c +@@ -652,16 +652,19 @@ _public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { + } + + static int has_cap(sd_bus_creds *c, size_t offset, int capability) { ++ unsigned long lc; + size_t sz; + + assert(c); + assert(capability >= 0); + assert(c->capability); + +- if ((unsigned) capability > cap_last_cap()) ++ lc = cap_last_cap(); ++ ++ if ((unsigned long) capability > lc) + return 0; + +- sz = DIV_ROUND_UP(cap_last_cap(), 32U); ++ sz = DIV_ROUND_UP(lc, 32LU); + + return !!(c->capability[offset * sz + CAP_TO_INDEX((uint32_t) capability)] & CAP_TO_MASK_CORRECTED((uint32_t) capability)); + } diff --git a/SOURCES/0187-mount-point-honour-AT_SYMLINK_FOLLOW-correctly.patch b/SOURCES/0187-mount-point-honour-AT_SYMLINK_FOLLOW-correctly.patch new file mode 100644 index 0000000..e6aff06 --- /dev/null +++ b/SOURCES/0187-mount-point-honour-AT_SYMLINK_FOLLOW-correctly.patch @@ -0,0 +1,26 @@ +From 24e6a5d1deac8aae11a6a3a22fb9b71cb77fdb33 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 8 Dec 2018 20:21:43 +0100 +Subject: [PATCH] mount-point: honour AT_SYMLINK_FOLLOW correctly + +Fixes: #11092 +(cherry picked from commit be24321f3dae91a166166b239954032727439942) + +Resolves: #1683319 +--- + src/basic/mount-util.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c +index ebe41a4c6c..3670b7f591 100644 +--- a/src/basic/mount-util.c ++++ b/src/basic/mount-util.c +@@ -109,7 +109,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id + if ((flags & AT_EMPTY_PATH) && isempty(filename)) + xsprintf(path, "/proc/self/fdinfo/%i", fd); + else { +- subfd = openat(fd, filename, O_CLOEXEC|O_PATH); ++ subfd = openat(fd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_FOLLOW ? 0 : O_NOFOLLOW)); + if (subfd < 0) + return -errno; + diff --git a/SOURCES/0188-travis-switch-from-trusty-to-xenial.patch b/SOURCES/0188-travis-switch-from-trusty-to-xenial.patch new file mode 100644 index 0000000..d60795c --- /dev/null +++ b/SOURCES/0188-travis-switch-from-trusty-to-xenial.patch @@ -0,0 +1,24 @@ +From 814bb6a8bc39e0f9900d84e564759a3a8698fb04 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Sat, 23 Feb 2019 22:28:05 +0100 +Subject: [PATCH] travis: switch from trusty to xenial + +This should fix the timeout in test-event + +Taken from: b635e4ef6b2f35f07111bb66a68fe850a2ccab72 + +Resolves: #1683319 +--- + .travis.yml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/.travis.yml b/.travis.yml +index 67677bdf06..0010da5784 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -1,4 +1,5 @@ + sudo: required ++dist: xenial + services: + - docker + diff --git a/SOURCES/0189-test-socket-util-Add-tests-for-receive_fd_iov-and-fr.patch b/SOURCES/0189-test-socket-util-Add-tests-for-receive_fd_iov-and-fr.patch new file mode 100644 index 0000000..1414f34 --- /dev/null +++ b/SOURCES/0189-test-socket-util-Add-tests-for-receive_fd_iov-and-fr.patch @@ -0,0 +1,262 @@ +From 8798fcef9052d2946637fa2d4376844fad6b5f8c Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Tue, 24 Jul 2018 20:15:55 -0700 +Subject: [PATCH] test-socket-util: Add tests for receive_fd_iov() and friends. + +Test it when sending an FD without any contents, or an FD and some contents, +or only contents and no FD (using a bare send().) + +Also fix the previous test which forked but was missing an _exit() at the +end of the child execution code. + +(cherry picked from commit 8a3386ab4fea9c4efa9c72e7c149cf510a46f03e) + +Resolves: #1683319 +--- + src/test/test-socket-util.c | 215 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 215 insertions(+) + +diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c +index 588485d881..19c5395b92 100644 +--- a/src/test/test-socket-util.c ++++ b/src/test/test-socket-util.c +@@ -6,8 +6,11 @@ + + #include "alloc-util.h" + #include "async.h" ++#include "exit-status.h" + #include "fd-util.h" ++#include "fileio.h" + #include "in-addr-util.h" ++#include "io-util.h" + #include "log.h" + #include "macro.h" + #include "process-util.h" +@@ -484,9 +487,215 @@ static void test_getpeercred_getpeergroups(void) { + } + + safe_close_pair(pair); ++ _exit(EXIT_SUCCESS); + } + } + ++static void test_passfd_read(void) { ++ static const char file_contents[] = "test contents for passfd"; ++ _cleanup_close_pair_ int pair[2] = { -1, -1 }; ++ int r; ++ ++ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); ++ ++ r = safe_fork("(passfd_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); ++ assert_se(r >= 0); ++ ++ if (r == 0) { ++ /* Child */ ++ char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX"; ++ _cleanup_close_ int tmpfd = -1; ++ ++ pair[0] = safe_close(pair[0]); ++ ++ tmpfd = mkostemp_safe(tmpfile); ++ assert_se(tmpfd >= 0); ++ assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents)); ++ tmpfd = safe_close(tmpfd); ++ ++ tmpfd = open(tmpfile, O_RDONLY); ++ assert_se(tmpfd >= 0); ++ assert_se(unlink(tmpfile) == 0); ++ ++ assert_se(send_one_fd(pair[1], tmpfd, MSG_DONTWAIT) == 0); ++ _exit(EXIT_SUCCESS); ++ } ++ ++ /* Parent */ ++ char buf[64]; ++ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1); ++ _cleanup_close_ int fd = -1; ++ ++ pair[1] = safe_close(pair[1]); ++ ++ assert_se(receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd) == 0); ++ ++ assert_se(fd >= 0); ++ r = read(fd, buf, sizeof(buf)-1); ++ assert_se(r >= 0); ++ buf[r] = 0; ++ assert_se(streq(buf, file_contents)); ++} ++ ++static void test_passfd_contents_read(void) { ++ _cleanup_close_pair_ int pair[2] = { -1, -1 }; ++ static const char file_contents[] = "test contents in the file"; ++ static const char wire_contents[] = "test contents on the wire"; ++ int r; ++ ++ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); ++ ++ r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); ++ assert_se(r >= 0); ++ ++ if (r == 0) { ++ /* Child */ ++ struct iovec iov = IOVEC_INIT_STRING(wire_contents); ++ char tmpfile[] = "/tmp/test-socket-util-passfd-contents-read-XXXXXX"; ++ _cleanup_close_ int tmpfd = -1; ++ ++ pair[0] = safe_close(pair[0]); ++ ++ tmpfd = mkostemp_safe(tmpfile); ++ assert_se(tmpfd >= 0); ++ assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents)); ++ tmpfd = safe_close(tmpfd); ++ ++ tmpfd = open(tmpfile, O_RDONLY); ++ assert_se(tmpfd >= 0); ++ assert_se(unlink(tmpfile) == 0); ++ ++ assert_se(send_one_fd_iov(pair[1], tmpfd, &iov, 1, MSG_DONTWAIT) > 0); ++ _exit(EXIT_SUCCESS); ++ } ++ ++ /* Parent */ ++ char buf[64]; ++ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1); ++ _cleanup_close_ int fd = -1; ++ ssize_t k; ++ ++ pair[1] = safe_close(pair[1]); ++ ++ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd); ++ assert_se(k > 0); ++ buf[k] = 0; ++ assert_se(streq(buf, wire_contents)); ++ ++ assert_se(fd >= 0); ++ r = read(fd, buf, sizeof(buf)-1); ++ assert_se(r >= 0); ++ buf[r] = 0; ++ assert_se(streq(buf, file_contents)); ++} ++ ++static void test_receive_nopassfd(void) { ++ _cleanup_close_pair_ int pair[2] = { -1, -1 }; ++ static const char wire_contents[] = "no fd passed here"; ++ int r; ++ ++ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); ++ ++ r = safe_fork("(receive_nopassfd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); ++ assert_se(r >= 0); ++ ++ if (r == 0) { ++ /* Child */ ++ struct iovec iov = IOVEC_INIT_STRING(wire_contents); ++ ++ pair[0] = safe_close(pair[0]); ++ ++ assert_se(send_one_fd_iov(pair[1], -1, &iov, 1, MSG_DONTWAIT) > 0); ++ _exit(EXIT_SUCCESS); ++ } ++ ++ /* Parent */ ++ char buf[64]; ++ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1); ++ int fd = -999; ++ ssize_t k; ++ ++ pair[1] = safe_close(pair[1]); ++ ++ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd); ++ assert_se(k > 0); ++ buf[k] = 0; ++ assert_se(streq(buf, wire_contents)); ++ ++ /* no fd passed here, confirm it was reset */ ++ assert_se(fd == -1); ++} ++ ++static void test_send_nodata_nofd(void) { ++ _cleanup_close_pair_ int pair[2] = { -1, -1 }; ++ int r; ++ ++ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); ++ ++ r = safe_fork("(send_nodata_nofd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); ++ assert_se(r >= 0); ++ ++ if (r == 0) { ++ /* Child */ ++ pair[0] = safe_close(pair[0]); ++ ++ assert_se(send_one_fd_iov(pair[1], -1, NULL, 0, MSG_DONTWAIT) == -EINVAL); ++ _exit(EXIT_SUCCESS); ++ } ++ ++ /* Parent */ ++ char buf[64]; ++ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1); ++ int fd = -999; ++ ssize_t k; ++ ++ pair[1] = safe_close(pair[1]); ++ ++ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd); ++ /* recvmsg() will return errno EAGAIN if nothing was sent */ ++ assert_se(k == -EAGAIN); ++ ++ /* receive_one_fd_iov returned error, so confirm &fd wasn't touched */ ++ assert_se(fd == -999); ++} ++ ++static void test_send_emptydata(void) { ++ _cleanup_close_pair_ int pair[2] = { -1, -1 }; ++ int r; ++ ++ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); ++ ++ r = safe_fork("(send_emptydata)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); ++ assert_se(r >= 0); ++ ++ if (r == 0) { ++ /* Child */ ++ struct iovec iov = IOVEC_INIT_STRING(""); /* zero-length iov */ ++ assert_se(iov.iov_len == 0); ++ ++ pair[0] = safe_close(pair[0]); ++ ++ /* This will succeed, since iov is set. */ ++ assert_se(send_one_fd_iov(pair[1], -1, &iov, 1, MSG_DONTWAIT) == 0); ++ _exit(EXIT_SUCCESS); ++ } ++ ++ /* Parent */ ++ char buf[64]; ++ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1); ++ int fd = -999; ++ ssize_t k; ++ ++ pair[1] = safe_close(pair[1]); ++ ++ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd); ++ /* receive_one_fd_iov() returns -EIO if an fd is not found and no data was returned. */ ++ assert_se(k == -EIO); ++ ++ /* receive_one_fd_iov returned error, so confirm &fd wasn't touched */ ++ assert_se(fd == -999); ++} ++ + int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); +@@ -515,5 +724,11 @@ int main(int argc, char *argv[]) { + + test_getpeercred_getpeergroups(); + ++ test_passfd_read(); ++ test_passfd_contents_read(); ++ test_receive_nopassfd(); ++ test_send_nodata_nofd(); ++ test_send_emptydata(); ++ + return 0; + } diff --git a/SOURCES/0190-socket-util-Introduce-send_one_fd_iov-and-receive_on.patch b/SOURCES/0190-socket-util-Introduce-send_one_fd_iov-and-receive_on.patch new file mode 100644 index 0000000..5225b97 --- /dev/null +++ b/SOURCES/0190-socket-util-Introduce-send_one_fd_iov-and-receive_on.patch @@ -0,0 +1,289 @@ +From bc7c550c444210aa8decf98ac0c1dcd051fcc532 Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Tue, 24 Jul 2018 18:46:01 -0700 +Subject: [PATCH] socket-util: Introduce send_one_fd_iov() and + receive_one_fd_iov() + +These take a struct iovec to send data together with the passed FD. + +The receive function returns the FD through an output argument. In case data is +received, but no FD is passed, the receive function will set the output +argument to -1 explicitly. + +Update code in dynamic-user to use the new helpers. + +(cherry picked from commit d34673ecb825aa9ecf6958b0caab792f5061c56a) + +Resolves: #1683319 +--- + src/basic/socket-util.c | 97 ++++++++++++++++++++++++++++++++--------- + src/basic/socket-util.h | 10 ++++- + src/core/dynamic-user.c | 57 ++---------------------- + 3 files changed, 90 insertions(+), 74 deletions(-) + +diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c +index 3f90a81d35..986bc6e67f 100644 +--- a/src/basic/socket-util.c ++++ b/src/basic/socket-util.c +@@ -1011,9 +1011,10 @@ int getpeergroups(int fd, gid_t **ret) { + return (int) n; + } + +-int send_one_fd_sa( ++ssize_t send_one_fd_iov_sa( + int transport_fd, + int fd, ++ struct iovec *iov, size_t iovlen, + const struct sockaddr *sa, socklen_t len, + int flags) { + +@@ -1024,28 +1025,58 @@ int send_one_fd_sa( + struct msghdr mh = { + .msg_name = (struct sockaddr*) sa, + .msg_namelen = len, +- .msg_control = &control, +- .msg_controllen = sizeof(control), ++ .msg_iov = iov, ++ .msg_iovlen = iovlen, + }; +- struct cmsghdr *cmsg; ++ ssize_t k; + + assert(transport_fd >= 0); +- assert(fd >= 0); + +- cmsg = CMSG_FIRSTHDR(&mh); +- cmsg->cmsg_level = SOL_SOCKET; +- cmsg->cmsg_type = SCM_RIGHTS; +- cmsg->cmsg_len = CMSG_LEN(sizeof(int)); +- memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); ++ /* ++ * We need either an FD or data to send. ++ * If there's nothing, return an error. ++ */ ++ if (fd < 0 && !iov) ++ return -EINVAL; + +- mh.msg_controllen = CMSG_SPACE(sizeof(int)); +- if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0) +- return -errno; ++ if (fd >= 0) { ++ struct cmsghdr *cmsg; + +- return 0; ++ mh.msg_control = &control; ++ mh.msg_controllen = sizeof(control); ++ ++ cmsg = CMSG_FIRSTHDR(&mh); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ cmsg->cmsg_len = CMSG_LEN(sizeof(int)); ++ memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); ++ ++ mh.msg_controllen = CMSG_SPACE(sizeof(int)); ++ } ++ k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags); ++ if (k < 0) ++ return (ssize_t) -errno; ++ ++ return k; + } + +-int receive_one_fd(int transport_fd, int flags) { ++int send_one_fd_sa( ++ int transport_fd, ++ int fd, ++ const struct sockaddr *sa, socklen_t len, ++ int flags) { ++ ++ assert(fd >= 0); ++ ++ return (int) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, sa, len, flags); ++} ++ ++ssize_t receive_one_fd_iov( ++ int transport_fd, ++ struct iovec *iov, size_t iovlen, ++ int flags, ++ int *ret_fd) { ++ + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; +@@ -1053,10 +1084,14 @@ int receive_one_fd(int transport_fd, int flags) { + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), ++ .msg_iov = iov, ++ .msg_iovlen = iovlen, + }; + struct cmsghdr *cmsg, *found = NULL; ++ ssize_t k; + + assert(transport_fd >= 0); ++ assert(ret_fd); + + /* + * Receive a single FD via @transport_fd. We don't care for +@@ -1066,8 +1101,9 @@ int receive_one_fd(int transport_fd, int flags) { + * combination with send_one_fd(). + */ + +- if (recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags) < 0) +- return -errno; ++ k = recvmsg(transport_fd, &mh, MSG_CMSG_CLOEXEC | flags); ++ if (k < 0) ++ return (ssize_t) -errno; + + CMSG_FOREACH(cmsg, &mh) { + if (cmsg->cmsg_level == SOL_SOCKET && +@@ -1079,12 +1115,33 @@ int receive_one_fd(int transport_fd, int flags) { + } + } + +- if (!found) { ++ if (!found) + cmsg_close_all(&mh); ++ ++ /* If didn't receive an FD or any data, return an error. */ ++ if (k == 0 && !found) + return -EIO; +- } + +- return *(int*) CMSG_DATA(found); ++ if (found) ++ *ret_fd = *(int*) CMSG_DATA(found); ++ else ++ *ret_fd = -1; ++ ++ return k; ++} ++ ++int receive_one_fd(int transport_fd, int flags) { ++ int fd; ++ ssize_t k; ++ ++ k = receive_one_fd_iov(transport_fd, NULL, 0, flags, &fd); ++ if (k == 0) ++ return fd; ++ ++ /* k must be negative, since receive_one_fd_iov() only returns ++ * a positive value if data was received through the iov. */ ++ assert(k < 0); ++ return (int) k; + } + + ssize_t next_datagram_size_fd(int fd) { +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 8e23cf2dbd..82781a0de1 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -130,11 +130,19 @@ int getpeercred(int fd, struct ucred *ucred); + int getpeersec(int fd, char **ret); + int getpeergroups(int fd, gid_t **ret); + ++ssize_t send_one_fd_iov_sa( ++ int transport_fd, ++ int fd, ++ struct iovec *iov, size_t iovlen, ++ const struct sockaddr *sa, socklen_t len, ++ int flags); + int send_one_fd_sa(int transport_fd, + int fd, + const struct sockaddr *sa, socklen_t len, + int flags); +-#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags) ++#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags) ++#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags) ++ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd); + int receive_one_fd(int transport_fd, int flags); + + ssize_t next_datagram_size_fd(int fd); +diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c +index 7c5111ddf6..021fd93a76 100644 +--- a/src/core/dynamic-user.c ++++ b/src/core/dynamic-user.c +@@ -312,20 +312,8 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) { + static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) { + uid_t uid = UID_INVALID; + struct iovec iov = IOVEC_INIT(&uid, sizeof(uid)); +- union { +- struct cmsghdr cmsghdr; +- uint8_t buf[CMSG_SPACE(sizeof(int))]; +- } control = {}; +- struct msghdr mh = { +- .msg_control = &control, +- .msg_controllen = sizeof(control), +- .msg_iov = &iov, +- .msg_iovlen = 1, +- }; +- struct cmsghdr *cmsg; +- ++ int lock_fd; + ssize_t k; +- int lock_fd = -1; + + assert(d); + assert(ret_uid); +@@ -334,15 +322,9 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) { + /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock + * on the socket taken. */ + +- k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); ++ k = receive_one_fd_iov(d->storage_socket[0], &iov, 1, MSG_DONTWAIT, &lock_fd); + if (k < 0) +- return -errno; +- +- cmsg = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int))); +- if (cmsg) +- lock_fd = *(int*) CMSG_DATA(cmsg); +- else +- cmsg_close_all(&mh); /* just in case... */ ++ return (int) k; + + *ret_uid = uid; + *ret_lock_fd = lock_fd; +@@ -352,42 +334,11 @@ static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) { + + static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) { + struct iovec iov = IOVEC_INIT(&uid, sizeof(uid)); +- union { +- struct cmsghdr cmsghdr; +- uint8_t buf[CMSG_SPACE(sizeof(int))]; +- } control = {}; +- struct msghdr mh = { +- .msg_control = &control, +- .msg_controllen = sizeof(control), +- .msg_iov = &iov, +- .msg_iovlen = 1, +- }; +- ssize_t k; + + assert(d); + + /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */ +- +- if (lock_fd >= 0) { +- struct cmsghdr *cmsg; +- +- cmsg = CMSG_FIRSTHDR(&mh); +- cmsg->cmsg_level = SOL_SOCKET; +- cmsg->cmsg_type = SCM_RIGHTS; +- cmsg->cmsg_len = CMSG_LEN(sizeof(int)); +- memcpy(CMSG_DATA(cmsg), &lock_fd, sizeof(int)); +- +- mh.msg_controllen = CMSG_SPACE(sizeof(int)); +- } else { +- mh.msg_control = NULL; +- mh.msg_controllen = 0; +- } +- +- k = sendmsg(d->storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL); +- if (k < 0) +- return -errno; +- +- return 0; ++ return send_one_fd_iov(d->storage_socket[1], lock_fd, &iov, 1, MSG_DONTWAIT); + } + + static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) { diff --git a/SOURCES/0191-core-swap-order-of-n_storage_fds-and-n_socket_fds-pa.patch b/SOURCES/0191-core-swap-order-of-n_storage_fds-and-n_socket_fds-pa.patch new file mode 100644 index 0000000..bd21f12 --- /dev/null +++ b/SOURCES/0191-core-swap-order-of-n_storage_fds-and-n_socket_fds-pa.patch @@ -0,0 +1,184 @@ +From 5e75fbf0ccc427fbe5151ab2096f75dcad5b00e7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 5 Jul 2018 09:56:54 +0200 +Subject: [PATCH] core: swap order of "n_storage_fds" and "n_socket_fds" + parameters + +When process fd lists to pass to activated programs we always place the +socket activation fds first, and the storage fds last. Irritatingly in +almost all calls the "n_storage_fds" parameter (i.e. the number of +storage fds to pass) came first so far, and the "n_socket_fds" parameter +second. Let's clean this up, and specify the number of fds in the order +the fds themselves are passed. + +(Also, let's fix one more case where "unsigned" was used to size an +array, while we should use "size_t" instead.) + +(cherry picked from commit 25b583d7ffd699384435eba8e49f6ce927a83af0) + +Resolves: #1683334 +--- + src/core/execute.c | 14 +++++++------- + src/core/execute.h | 2 +- + src/core/service.c | 25 ++++++++++++++----------- + 3 files changed, 22 insertions(+), 19 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index ffb92ddfc7..7476ac51da 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -147,11 +147,11 @@ static int shift_fds(int fds[], size_t n_fds) { + return 0; + } + +-static int flags_fds(const int fds[], size_t n_storage_fds, size_t n_socket_fds, bool nonblock) { ++static int flags_fds(const int fds[], size_t n_socket_fds, size_t n_storage_fds, bool nonblock) { + size_t i, n_fds; + int r; + +- n_fds = n_storage_fds + n_socket_fds; ++ n_fds = n_socket_fds + n_storage_fds; + if (n_fds <= 0) + return 0; + +@@ -2718,8 +2718,8 @@ static int exec_child( + int socket_fd, + int named_iofds[3], + int *fds, +- size_t n_storage_fds, + size_t n_socket_fds, ++ size_t n_storage_fds, + char **files_env, + int user_lookup_fd, + int *exit_status) { +@@ -3171,7 +3171,7 @@ static int exec_child( + if (r >= 0) + r = shift_fds(fds, n_fds); + if (r >= 0) +- r = flags_fds(fds, n_storage_fds, n_socket_fds, context->non_blocking); ++ r = flags_fds(fds, n_socket_fds, n_storage_fds, context->non_blocking); + if (r < 0) { + *exit_status = EXIT_FDS; + return log_unit_error_errno(unit, r, "Failed to adjust passed file descriptors: %m"); +@@ -3449,7 +3449,7 @@ int exec_spawn(Unit *unit, + assert(context); + assert(ret); + assert(params); +- assert(params->fds || (params->n_storage_fds + params->n_socket_fds <= 0)); ++ assert(params->fds || (params->n_socket_fds + params->n_storage_fds <= 0)); + + if (context->std_input == EXEC_INPUT_SOCKET || + context->std_output == EXEC_OUTPUT_SOCKET || +@@ -3469,8 +3469,8 @@ int exec_spawn(Unit *unit, + } else { + socket_fd = -1; + fds = params->fds; +- n_storage_fds = params->n_storage_fds; + n_socket_fds = params->n_socket_fds; ++ n_storage_fds = params->n_storage_fds; + } + + r = exec_context_named_iofds(context, params, named_iofds); +@@ -3509,8 +3509,8 @@ int exec_spawn(Unit *unit, + socket_fd, + named_iofds, + fds, +- n_storage_fds, + n_socket_fds, ++ n_storage_fds, + files_env, + unit->manager->user_lookup_fds[1], + &exit_status); +diff --git a/src/core/execute.h b/src/core/execute.h +index 77ffe82323..49705e0d3a 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -296,8 +296,8 @@ struct ExecParameters { + + int *fds; + char **fd_names; +- size_t n_storage_fds; + size_t n_socket_fds; ++ size_t n_storage_fds; + + ExecFlags flags; + bool selinux_context_net:1; +diff --git a/src/core/service.c b/src/core/service.c +index db17221888..7f8ce1b998 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -1178,21 +1178,23 @@ static int service_coldplug(Unit *u) { + return 0; + } + +-static int service_collect_fds(Service *s, +- int **fds, +- char ***fd_names, +- unsigned *n_storage_fds, +- unsigned *n_socket_fds) { ++static int service_collect_fds( ++ Service *s, ++ int **fds, ++ char ***fd_names, ++ size_t *n_socket_fds, ++ size_t *n_storage_fds) { + + _cleanup_strv_free_ char **rfd_names = NULL; + _cleanup_free_ int *rfds = NULL; +- unsigned rn_socket_fds = 0, rn_storage_fds = 0; ++ size_t rn_socket_fds = 0, rn_storage_fds = 0; + int r; + + assert(s); + assert(fds); + assert(fd_names); + assert(n_socket_fds); ++ assert(n_storage_fds); + + if (s->socket_fd >= 0) { + +@@ -1256,7 +1258,7 @@ static int service_collect_fds(Service *s, + + if (s->n_fd_store > 0) { + ServiceFDStore *fs; +- unsigned n_fds; ++ size_t n_fds; + char **nl; + int *t; + +@@ -1325,9 +1327,10 @@ static int service_spawn( + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, ++ .exec_fd = -1, + }; + _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL; +- unsigned n_storage_fds = 0, n_socket_fds = 0, n_env = 0; ++ size_t n_socket_fds = 0, n_storage_fds = 0, n_env = 0; + _cleanup_free_ int *fds = NULL; + pid_t pid; + int r; +@@ -1353,11 +1356,11 @@ static int service_spawn( + s->exec_context.std_output == EXEC_OUTPUT_SOCKET || + s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { + +- r = service_collect_fds(s, &fds, &fd_names, &n_storage_fds, &n_socket_fds); ++ r = service_collect_fds(s, &fds, &fd_names, &n_socket_fds, &n_storage_fds); + if (r < 0) + return r; + +- log_unit_debug(UNIT(s), "Passing %i fds to service", n_storage_fds + n_socket_fds); ++ log_unit_debug(UNIT(s), "Passing %zu fds to service", n_socket_fds + n_storage_fds); + } + + r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout)); +@@ -1450,8 +1453,8 @@ static int service_spawn( + exec_params.environment = final_env; + exec_params.fds = fds; + exec_params.fd_names = fd_names; +- exec_params.n_storage_fds = n_storage_fds; + exec_params.n_socket_fds = n_socket_fds; ++ exec_params.n_storage_fds = n_storage_fds; + exec_params.watchdog_usec = s->watchdog_usec; + exec_params.selinux_context_net = s->socket_fd_selinux_context_net; + if (s->type == SERVICE_IDLE) diff --git a/SOURCES/0192-execute-use-our-usual-syntax-for-defining-bit-masks.patch b/SOURCES/0192-execute-use-our-usual-syntax-for-defining-bit-masks.patch new file mode 100644 index 0000000..325203d --- /dev/null +++ b/SOURCES/0192-execute-use-our-usual-syntax-for-defining-bit-masks.patch @@ -0,0 +1,31 @@ +From 8c937623864721345a5789d664775416723b6614 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 5 Jul 2018 10:00:52 +0200 +Subject: [PATCH] execute: use our usual syntax for defining bit masks + +(cherry picked from commit ce0d60a7c4e07c5bdfed9f076bd48752287f0777) + +Resolves: #1683334 +--- + src/core/execute.h | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/core/execute.h b/src/core/execute.h +index 49705e0d3a..f24dbf581a 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -85,10 +85,10 @@ struct ExecStatus { + }; + + typedef enum ExecCommandFlags { +- EXEC_COMMAND_IGNORE_FAILURE = 1, +- EXEC_COMMAND_FULLY_PRIVILEGED = 2, +- EXEC_COMMAND_NO_SETUID = 4, +- EXEC_COMMAND_AMBIENT_MAGIC = 8, ++ EXEC_COMMAND_IGNORE_FAILURE = 1 << 0, ++ EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1, ++ EXEC_COMMAND_NO_SETUID = 1 << 2, ++ EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3, + } ExecCommandFlags; + + struct ExecCommand { diff --git a/SOURCES/0193-core-introduce-new-Type-exec-service-type.patch b/SOURCES/0193-core-introduce-new-Type-exec-service-type.patch new file mode 100644 index 0000000..866af38 --- /dev/null +++ b/SOURCES/0193-core-introduce-new-Type-exec-service-type.patch @@ -0,0 +1,572 @@ +From c7861c541e49e0bf3678d9f3c9093ee819ed436a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 17 Jul 2018 11:47:14 +0200 +Subject: [PATCH] core: introduce new Type=exec service type + +Users are often surprised that "systemd-run" command lines like +"systemd-run -p User=idontexist /bin/true" will return successfully, +even though the logs show that the process couldn't be invoked, as the +user "idontexist" doesn't exist. This is because Type=simple will only +wait until fork() succeeded before returning start-up success. + +This patch adds a new service type Type=exec, which is very similar to +Type=simple, but waits until the child process completed the execve() +before returning success. It uses a pipe that has O_CLOEXEC set for this +logic, so that the kernel automatically sends POLLHUP on it when the +execve() succeeded but leaves the pipe open if not. This means PID 1 +waits exactly until the execve() succeeded in the child, and not longer +and not shorter, which is the desired functionality. + +Making use of this new functionality, the command line +"systemd-run -p User=idontexist -p Type=exec /bin/true" will now fail, +as expected. + +(cherry picked from commit 5686391b006ee82d8a4559067ad9818e3e631247) + +Resolves: #1683334 +--- + src/core/execute.c | 89 +++++++++++++++++++++--- + src/core/execute.h | 3 + + src/core/mount.c | 9 +-- + src/core/service.c | 167 ++++++++++++++++++++++++++++++++++++++++++--- + src/core/service.h | 4 ++ + src/core/socket.c | 9 +-- + src/core/swap.c | 1 + + 7 files changed, 254 insertions(+), 28 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 7476ac51da..c62f3cf849 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2566,6 +2566,7 @@ static int close_remaining_fds( + const DynamicCreds *dcreds, + int user_lookup_fd, + int socket_fd, ++ int exec_fd, + int *fds, size_t n_fds) { + + size_t n_dont_close = 0; +@@ -2582,6 +2583,8 @@ static int close_remaining_fds( + + if (socket_fd >= 0) + dont_close[n_dont_close++] = socket_fd; ++ if (exec_fd >= 0) ++ dont_close[n_dont_close++] = exec_fd; + if (n_fds > 0) { + memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds); + n_dont_close += n_fds; +@@ -2725,9 +2728,10 @@ static int exec_child( + int *exit_status) { + + _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; +- _cleanup_free_ char *home_buffer = NULL; ++ int *fds_with_exec_fd, n_fds_with_exec_fd, r, ngids = 0, exec_fd = -1; + _cleanup_free_ gid_t *supplementary_gids = NULL; + const char *username = NULL, *groupname = NULL; ++ _cleanup_free_ char *home_buffer = NULL; + const char *home = NULL, *shell = NULL; + dev_t journal_stream_dev = 0; + ino_t journal_stream_ino = 0; +@@ -2747,7 +2751,6 @@ static int exec_child( + #endif + uid_t uid = UID_INVALID; + gid_t gid = GID_INVALID; +- int r, ngids = 0; + size_t n_fds; + ExecDirectoryType dt; + int secure_bits; +@@ -2791,8 +2794,8 @@ static int exec_child( + /* In case anything used libc syslog(), close this here, too */ + closelog(); + +- n_fds = n_storage_fds + n_socket_fds; +- r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds); ++ n_fds = n_socket_fds + n_storage_fds; ++ r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, params->exec_fd, fds, n_fds); + if (r < 0) { + *exit_status = EXIT_FDS; + return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m"); +@@ -3165,9 +3168,45 @@ static int exec_child( + } + + /* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are +- * more aggressive this time since socket_fd and the netns fds we don't need anymore. The custom endpoint fd +- * was needed to upload the policy and can now be closed as well. */ +- r = close_all_fds(fds, n_fds); ++ * more aggressive this time since socket_fd and the netns fds we don't need anymore. We do keep the exec_fd ++ * however if we have it as we want to keep it open until the final execve(). */ ++ ++ if (params->exec_fd >= 0) { ++ exec_fd = params->exec_fd; ++ ++ if (exec_fd < 3 + (int) n_fds) { ++ int moved_fd; ++ ++ /* Let's move the exec fd far up, so that it's outside of the fd range we want to pass to the ++ * process we are about to execute. */ ++ ++ moved_fd = fcntl(exec_fd, F_DUPFD_CLOEXEC, 3 + (int) n_fds); ++ if (moved_fd < 0) { ++ *exit_status = EXIT_FDS; ++ return log_unit_error_errno(unit, errno, "Couldn't move exec fd up: %m"); ++ } ++ ++ safe_close(exec_fd); ++ exec_fd = moved_fd; ++ } else { ++ /* This fd should be FD_CLOEXEC already, but let's make sure. */ ++ r = fd_cloexec(exec_fd, true); ++ if (r < 0) { ++ *exit_status = EXIT_FDS; ++ return log_unit_error_errno(unit, r, "Failed to make exec fd FD_CLOEXEC: %m"); ++ } ++ } ++ ++ fds_with_exec_fd = newa(int, n_fds + 1); ++ memcpy(fds_with_exec_fd, fds, n_fds * sizeof(int)); ++ fds_with_exec_fd[n_fds] = exec_fd; ++ n_fds_with_exec_fd = n_fds + 1; ++ } else { ++ fds_with_exec_fd = fds; ++ n_fds_with_exec_fd = n_fds; ++ } ++ ++ r = close_all_fds(fds_with_exec_fd, n_fds_with_exec_fd); + if (r >= 0) + r = shift_fds(fds, n_fds); + if (r >= 0) +@@ -3177,6 +3216,11 @@ static int exec_child( + return log_unit_error_errno(unit, r, "Failed to adjust passed file descriptors: %m"); + } + ++ /* At this point, the fds we want to pass to the program are all ready and set up, with O_CLOEXEC turned off ++ * and at the right fd numbers. The are no other fds open, with one exception: the exec_fd if it is defined, ++ * and it has O_CLOEXEC set, after all we want it to be closed by the execve(), so that our parent knows we ++ * came this far. */ ++ + secure_bits = context->secure_bits; + + if (needs_sandboxing) { +@@ -3407,10 +3451,35 @@ static int exec_child( + LOG_UNIT_INVOCATION_ID(unit)); + } + ++ if (exec_fd >= 0) { ++ uint8_t hot = 1; ++ ++ /* We have finished with all our initializations. Let's now let the manager know that. From this point ++ * on, if the manager sees POLLHUP on the exec_fd, then execve() was successful. */ ++ ++ if (write(exec_fd, &hot, sizeof(hot)) < 0) { ++ *exit_status = EXIT_EXEC; ++ return log_unit_error_errno(unit, errno, "Failed to enable exec_fd: %m"); ++ } ++ } ++ + execve(command->path, final_argv, accum_env); ++ r = -errno; ++ ++ if (exec_fd >= 0) { ++ uint8_t hot = 0; ++ ++ /* The execve() failed. This means the exec_fd is still open. Which means we need to tell the manager ++ * that POLLHUP on it no longer means execve() succeeded. */ ++ ++ if (write(exec_fd, &hot, sizeof(hot)) < 0) { ++ *exit_status = EXIT_EXEC; ++ return log_unit_error_errno(unit, errno, "Failed to disable exec_fd: %m"); ++ } ++ } + +- if (errno == ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) { +- log_struct_errno(LOG_INFO, errno, ++ if (r == -ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) { ++ log_struct_errno(LOG_INFO, r, + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, + LOG_UNIT_ID(unit), + LOG_UNIT_INVOCATION_ID(unit), +@@ -3421,7 +3490,7 @@ static int exec_child( + } + + *exit_status = EXIT_EXEC; +- return log_unit_error_errno(unit, errno, "Failed to execute command: %m"); ++ return log_unit_error_errno(unit, r, "Failed to execute command: %m"); + } + + static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l); +diff --git a/src/core/execute.h b/src/core/execute.h +index f24dbf581a..bff1634b88 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -316,6 +316,9 @@ struct ExecParameters { + int stdin_fd; + int stdout_fd; + int stderr_fd; ++ ++ /* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done */ ++ int exec_fd; + }; + + #include "unit.h" +diff --git a/src/core/mount.c b/src/core/mount.c +index 21437dad08..16229d4af1 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -747,10 +747,11 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { + static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { + + ExecParameters exec_params = { +- .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, +- .stdin_fd = -1, +- .stdout_fd = -1, +- .stderr_fd = -1, ++ .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, ++ .stdin_fd = -1, ++ .stdout_fd = -1, ++ .stderr_fd = -1, ++ .exec_fd = -1, + }; + pid_t pid; + int r; +diff --git a/src/core/service.c b/src/core/service.c +index 7f8ce1b998..3eab749362 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -79,9 +79,10 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING + }; + +-static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata); ++static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata); + static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); + static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata); ++static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata); + + static void service_enter_signal(Service *s, ServiceState state, ServiceResult f); + static void service_enter_reload_by_notify(Service *s); +@@ -389,6 +390,7 @@ static void service_done(Unit *u) { + service_stop_watchdog(s); + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); ++ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source); + + service_release_resources(u); + } +@@ -1066,6 +1068,9 @@ static void service_set_state(Service *s, ServiceState state) { + !(state == SERVICE_DEAD && UNIT(s)->job)) + service_close_socket_fd(s); + ++ if (state != SERVICE_START) ++ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source); ++ + if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + service_stop_watchdog(s); + +@@ -1296,6 +1301,63 @@ static int service_collect_fds( + return 0; + } + ++static int service_allocate_exec_fd_event_source( ++ Service *s, ++ int fd, ++ sd_event_source **ret_event_source) { ++ ++ _cleanup_(sd_event_source_unrefp) sd_event_source *source = NULL; ++ int r; ++ ++ assert(s); ++ assert(fd >= 0); ++ assert(ret_event_source); ++ ++ r = sd_event_add_io(UNIT(s)->manager->event, &source, fd, 0, service_dispatch_exec_io, s); ++ if (r < 0) ++ return log_unit_error_errno(UNIT(s), r, "Failed to allocate exec_fd event source: %m"); ++ ++ /* This is a bit lower priority than SIGCHLD, as that carries a lot more interesting failure information */ ++ ++ r = sd_event_source_set_priority(source, SD_EVENT_PRIORITY_NORMAL-3); ++ if (r < 0) ++ return log_unit_error_errno(UNIT(s), r, "Failed to adjust priority of exec_fd event source: %m"); ++ ++ (void) sd_event_source_set_description(source, "service event_fd"); ++ ++ r = sd_event_source_set_io_fd_own(source, true); ++ if (r < 0) ++ return log_unit_error_errno(UNIT(s), r, "Failed to pass ownership of fd to event source: %m"); ++ ++ *ret_event_source = TAKE_PTR(source); ++ return 0; ++} ++ ++static int service_allocate_exec_fd( ++ Service *s, ++ sd_event_source **ret_event_source, ++ int* ret_exec_fd) { ++ ++ _cleanup_close_pair_ int p[2] = { -1, -1 }; ++ int r; ++ ++ assert(s); ++ assert(ret_event_source); ++ assert(ret_exec_fd); ++ ++ if (pipe2(p, O_CLOEXEC|O_NONBLOCK) < 0) ++ return log_unit_error_errno(UNIT(s), errno, "Failed to allocate exec_fd pipe: %m"); ++ ++ r = service_allocate_exec_fd_event_source(s, p[0], ret_event_source); ++ if (r < 0) ++ return r; ++ ++ p[0] = -1; ++ *ret_exec_fd = TAKE_FD(p[1]); ++ ++ return 0; ++} ++ + static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) { + assert(s); + +@@ -1330,7 +1392,9 @@ static int service_spawn( + .exec_fd = -1, + }; + _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL; ++ _cleanup_(sd_event_source_unrefp) sd_event_source *exec_fd_source = NULL; + size_t n_socket_fds = 0, n_storage_fds = 0, n_env = 0; ++ _cleanup_close_ int exec_fd = -1; + _cleanup_free_ int *fds = NULL; + pid_t pid; + int r; +@@ -1363,6 +1427,14 @@ static int service_spawn( + log_unit_debug(UNIT(s), "Passing %zu fds to service", n_socket_fds + n_storage_fds); + } + ++ if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) { ++ assert(!s->exec_fd_event_source); ++ ++ r = service_allocate_exec_fd(s, &exec_fd_source, &exec_fd); ++ if (r < 0) ++ return r; ++ } ++ + r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout)); + if (r < 0) + return r; +@@ -1462,6 +1534,7 @@ static int service_spawn( + exec_params.stdin_fd = s->stdin_fd; + exec_params.stdout_fd = s->stdout_fd; + exec_params.stderr_fd = s->stderr_fd; ++ exec_params.exec_fd = exec_fd; + + r = exec_spawn(UNIT(s), + c, +@@ -1473,6 +1546,9 @@ static int service_spawn( + if (r < 0) + return r; + ++ s->exec_fd_event_source = TAKE_PTR(exec_fd_source); ++ s->exec_fd_hot = false; ++ + r = unit_watch_pid(UNIT(s), pid); + if (r < 0) /* FIXME: we need to do something here */ + return r; +@@ -1984,14 +2060,12 @@ static void service_enter_start(Service *s) { + s->control_pid = pid; + service_set_state(s, SERVICE_START); + +- } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) { ++ } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_EXEC)) { + +- /* For oneshot services we wait until the start +- * process exited, too, but it is our main process. */ ++ /* For oneshot services we wait until the start process exited, too, but it is our main process. */ + +- /* For D-Bus services we know the main pid right away, +- * but wait for the bus name to appear on the +- * bus. Notify services are similar. */ ++ /* For D-Bus services we know the main pid right away, but wait for the bus name to appear on the ++ * bus. 'notify' and 'exec' services are similar. */ + + service_set_main_pid(s, pid); + service_set_state(s, SERVICE_START); +@@ -2444,6 +2518,13 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { + if (r < 0) + return r; + ++ if (s->exec_fd_event_source) { ++ r = unit_serialize_item_fd(u, f, fds, "exec-fd", sd_event_source_get_io_fd(s->exec_fd_event_source)); ++ if (r < 0) ++ return r; ++ unit_serialize_item(u, f, "exec-fd-hot", yes_no(s->exec_fd_hot)); ++ } ++ + if (UNIT_ISSET(s->accept_socket)) { + r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id); + if (r < 0) +@@ -2777,6 +2858,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, + s->stderr_fd = fdset_remove(fds, fd); + s->exec_context.stdio_as_fds = true; + } ++ } else if (streq(key, "exec-fd")) { ++ int fd; ++ ++ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) ++ log_unit_debug(u, "Failed to parse exec-fd value: %s", value); ++ else { ++ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source); ++ ++ fd = fdset_remove(fds, fd); ++ if (service_allocate_exec_fd_event_source(s, fd, &s->exec_fd_event_source) < 0) ++ safe_close(fd); ++ } + } else if (streq(key, "watchdog-override-usec")) { + usec_t watchdog_override_usec; + if (timestamp_deserialize(value, &watchdog_override_usec) < 0) +@@ -2860,7 +2953,7 @@ static int service_watch_pid_file(Service *s) { + + log_unit_debug(UNIT(s), "Setting watch for PID file %s", s->pid_file_pathspec->path); + +- r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io); ++ r = path_spec_watch(s->pid_file_pathspec, service_dispatch_inotify_io); + if (r < 0) + goto fail; + +@@ -2904,7 +2997,7 @@ static int service_demand_pid_file(Service *s) { + return service_watch_pid_file(s); + } + +-static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, void *userdata) { ++static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata) { + PathSpec *p = userdata; + Service *s; + +@@ -2937,6 +3030,59 @@ fail: + return 0; + } + ++static int service_dispatch_exec_io(sd_event_source *source, int fd, uint32_t events, void *userdata) { ++ Service *s = SERVICE(userdata); ++ ++ assert(s); ++ ++ log_unit_debug(UNIT(s), "got exec-fd event"); ++ ++ /* If Type=exec is set, we'll consider a service started successfully the instant we invoked execve() ++ * successfully for it. We implement this through a pipe() towards the child, which the kernel automatically ++ * closes for us due to O_CLOEXEC on execve() in the child, which then triggers EOF on the pipe in the ++ * parent. We need to be careful however, as there are other reasons that we might cause the child's side of ++ * the pipe to be closed (for example, a simple exit()). To deal with that we'll ignore EOFs on the pipe unless ++ * the child signalled us first that it is about to call the execve(). It does so by sending us a simple ++ * non-zero byte via the pipe. We also provide the child with a way to inform us in case execve() failed: if it ++ * sends a zero byte we'll ignore POLLHUP on the fd again. */ ++ ++ for (;;) { ++ uint8_t x; ++ ssize_t n; ++ ++ n = read(fd, &x, sizeof(x)); ++ if (n < 0) { ++ if (errno == EAGAIN) /* O_NONBLOCK in effect → everything queued has now been processed. */ ++ return 0; ++ ++ return log_unit_error_errno(UNIT(s), errno, "Failed to read from exec_fd: %m"); ++ } ++ if (n == 0) { /* EOF → the event we are waiting for */ ++ ++ s->exec_fd_event_source = sd_event_source_unref(s->exec_fd_event_source); ++ ++ if (s->exec_fd_hot) { /* Did the child tell us to expect EOF now? */ ++ log_unit_debug(UNIT(s), "Got EOF on exec-fd"); ++ ++ s->exec_fd_hot = false; ++ ++ /* Nice! This is what we have been waiting for. Transition to next state. */ ++ if (s->type == SERVICE_EXEC && s->state == SERVICE_START) ++ service_enter_start_post(s); ++ } else ++ log_unit_debug(UNIT(s), "Got EOF on exec-fd while it was disabled, ignoring."); ++ ++ return 0; ++ } ++ ++ /* A byte was read → this turns on/off the exec fd logic */ ++ assert(n == sizeof(x)); ++ s->exec_fd_hot = x; ++ } ++ ++ return 0; ++} ++ + static void service_notify_cgroup_empty_event(Unit *u) { + Service *s = SERVICE(u); + +@@ -3850,7 +3996,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { + [SERVICE_ONESHOT] = "oneshot", + [SERVICE_DBUS] = "dbus", + [SERVICE_NOTIFY] = "notify", +- [SERVICE_IDLE] = "idle" ++ [SERVICE_IDLE] = "idle", ++ [SERVICE_EXEC] = "exec", + }; + + DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); +diff --git a/src/core/service.h b/src/core/service.h +index a142b09f0d..1206e3cdda 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -30,6 +30,7 @@ typedef enum ServiceType { + SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */ + SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */ + SERVICE_IDLE, /* much like simple, but delay exec() until all jobs are dispatched. */ ++ SERVICE_EXEC, /* we fork and wait until we execute exec() (this means our own setup is waited for) */ + _SERVICE_TYPE_MAX, + _SERVICE_TYPE_INVALID = -1 + } ServiceType; +@@ -165,6 +166,8 @@ struct Service { + NotifyAccess notify_access; + NotifyState notify_state; + ++ sd_event_source *exec_fd_event_source; ++ + ServiceFDStore *fd_store; + size_t n_fd_store; + unsigned n_fd_store_max; +@@ -179,6 +182,7 @@ struct Service { + + unsigned n_restarts; + bool flush_n_restarts; ++ bool exec_fd_hot; + }; + + extern const UnitVTable service_vtable; +diff --git a/src/core/socket.c b/src/core/socket.c +index 56d32225c4..d488c64e91 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -1867,10 +1867,11 @@ static int socket_coldplug(Unit *u) { + static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { + + ExecParameters exec_params = { +- .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, +- .stdin_fd = -1, +- .stdout_fd = -1, +- .stderr_fd = -1, ++ .flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, ++ .stdin_fd = -1, ++ .stdout_fd = -1, ++ .stderr_fd = -1, ++ .exec_fd = -1, + }; + pid_t pid; + int r; +diff --git a/src/core/swap.c b/src/core/swap.c +index b78b1aa266..e01e61e56d 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -606,6 +606,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, ++ .exec_fd = -1, + }; + pid_t pid; + int r; diff --git a/SOURCES/0194-man-document-the-new-Type-exec-type.patch b/SOURCES/0194-man-document-the-new-Type-exec-type.patch new file mode 100644 index 0000000..671277f --- /dev/null +++ b/SOURCES/0194-man-document-the-new-Type-exec-type.patch @@ -0,0 +1,207 @@ +From d77d5a9399e393734fe8c8a5ad085036775854a7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 17 Jul 2018 12:01:26 +0200 +Subject: [PATCH] man: document the new Type=exec type + +And while we are at it, let's rearrange and extend the Type= +documentation a bit. Let's make it an itemized list, and let's add a +paragraph explaining which type best to use. + +(cherry picked from commit 79905a246d645d21633f09f564b3672d5085a85c) + +Resolves: #1683334 +--- + man/systemd-run.xml | 10 +++ + man/systemd.service.xml | 158 ++++++++++++++++++++++------------------ + 2 files changed, 97 insertions(+), 71 deletions(-) + +diff --git a/man/systemd-run.xml b/man/systemd-run.xml +index 1c254afae3..a134b2c0dc 100644 +--- a/man/systemd-run.xml ++++ b/man/systemd-run.xml +@@ -83,6 +83,16 @@ + COMMAND may be omitted. In this case, systemd-run creates only a + .path, .socket, or .timer unit that triggers the + specified unit. ++ ++ By default, services created with systemd-run default to the type, ++ see the description of Type= in ++ systemd.service5 for ++ details. Note that when this type is used the service manager (and thus the systemd-run command) ++ considers service start-up successful as soon as the fork() for the main service process ++ succeeded, i.e. before the execve() is invoked, and thus even if the specified command cannot ++ be started. Consider using the service type (i.e. ) to ++ ensure that systemd-run returns successfully only if the specified command line has been ++ successfully started. + + + +diff --git a/man/systemd.service.xml b/man/systemd.service.xml +index add54524ce..315b80e704 100644 +--- a/man/systemd.service.xml ++++ b/man/systemd.service.xml +@@ -153,77 +153,93 @@ + + Type= + +- Configures the process start-up type for this +- service unit. One of +- , +- , +- , +- , +- or +- . +- +- If set to (the default if +- neither Type= nor +- BusName=, but ExecStart= +- are specified), it is expected that the process configured +- with ExecStart= is the main process of the +- service. In this mode, if the process offers functionality to +- other processes on the system, its communication channels +- should be installed before the daemon is started up (e.g. +- sockets set up by systemd, via socket activation), as systemd +- will immediately proceed starting follow-up units. +- +- If set to , it is expected that +- the process configured with ExecStart= will +- call fork() as part of its start-up. The +- parent process is expected to exit when start-up is complete +- and all communication channels are set up. The child continues +- to run as the main daemon process. This is the behavior of +- traditional UNIX daemons. If this setting is used, it is +- recommended to also use the PIDFile= +- option, so that systemd can identify the main process of the +- daemon. systemd will proceed with starting follow-up units as +- soon as the parent process exits. +- +- Behavior of is similar to +- ; however, it is expected that the +- process has to exit before systemd starts follow-up units. +- RemainAfterExit= is particularly useful for +- this type of service. This is the implied default if neither +- Type= nor ExecStart= are +- specified. +- +- Behavior of is similar to +- ; however, it is expected that the +- daemon acquires a name on the D-Bus bus, as configured by +- BusName=. systemd will proceed with +- starting follow-up units after the D-Bus bus name has been +- acquired. Service units with this option configured implicitly +- gain dependencies on the dbus.socket +- unit. This type is the default if BusName= +- is specified. +- +- Behavior of is similar to +- ; however, it is expected that the +- daemon sends a notification message via +- sd_notify3 +- or an equivalent call when it has finished starting up. +- systemd will proceed with starting follow-up units after this +- notification message has been sent. If this option is used, +- NotifyAccess= (see below) should be set to +- open access to the notification socket provided by systemd. If +- NotifyAccess= is missing or set to +- , it will be forcibly set to +- . Note that currently +- Type= will not work +- if used in combination with +- PrivateNetwork=. +- +- Behavior of is very similar to ; however, actual execution +- of the service program is delayed until all active jobs are dispatched. This may be used to avoid interleaving +- of output of shell services with the status output on the console. Note that this type is useful only to +- improve console output, it is not useful as a general unit ordering tool, and the effect of this service type +- is subject to a 5s time-out, after which the service program is invoked anyway. ++ ++ Configures the process start-up type for this service unit. One of , ++ , , , , ++ or : ++ ++ ++ If set to (the default if ExecStart= is ++ specified but neither Type= nor BusName= are), the service manager ++ will consider the unit started immediately after the main service process has been forked off. It is ++ expected that the process configured with ExecStart= is the main process of the ++ service. In this mode, if the process offers functionality to other processes on the system, its ++ communication channels should be installed before the service is started up (e.g. sockets set up by ++ systemd, via socket activation), as the service manager will immediately proceed starting follow-up units, ++ right after creating the main service process, and before executing the service's binary. Note that this ++ means systemctl start command lines for services will report ++ success even if the service's binary cannot be invoked successfully (for example because the selected ++ User= doesn't exist, or the service binary is missing). ++ ++ The type is similar to , but the service ++ manager will consider the unit started immediately after the main service binary has been executed. The service ++ manager will delay starting of follow-up units until that point. (Or in other words: ++ proceeds with further jobs right after fork() returns, while ++ will not proceed before both fork() and ++ execve() in the service process succeeded.) Note that this means systemctl ++ start command lines for services will report failure when the service's ++ binary cannot be invoked successfully (for example because the selected User= doesn't ++ exist, or the service binary is missing). ++ ++ If set to , it is expected that the process configured with ++ ExecStart= will call fork() as part of its start-up. The parent ++ process is expected to exit when start-up is complete and all communication channels are set up. The child ++ continues to run as the main service process, and the service manager will consider the unit started when ++ the parent process exits. This is the behavior of traditional UNIX services. If this setting is used, it is ++ recommended to also use the PIDFile= option, so that systemd can reliably identify the ++ main process of the service. systemd will proceed with starting follow-up units as soon as the parent ++ process exits. ++ ++ Behavior of is similar to ; however, the ++ service manager will consider the unit started after the main process exits. It will then start follow-up ++ units. RemainAfterExit= is particularly useful for this type of ++ service. Type= is the implied default if neither ++ Type= nor ExecStart= are specified. ++ ++ Behavior of is similar to ; however, it is ++ expected that the service acquires a name on the D-Bus bus, as configured by ++ BusName=. systemd will proceed with starting follow-up units after the D-Bus bus name ++ has been acquired. Service units with this option configured implicitly gain dependencies on the ++ dbus.socket unit. This type is the default if BusName= is ++ specified. ++ ++ Behavior of is similar to ; however, it is ++ expected that the service sends a notification message via ++ sd_notify3 or an ++ equivalent call when it has finished starting up. systemd will proceed with starting follow-up units after ++ this notification message has been sent. If this option is used, NotifyAccess= (see ++ below) should be set to open access to the notification socket provided by systemd. If ++ NotifyAccess= is missing or set to , it will be forcibly set to ++ . Note that currently Type= will not work if ++ used in combination with PrivateNetwork=. ++ ++ Behavior of is very similar to ; however, ++ actual execution of the service program is delayed until all active jobs are dispatched. This may be used ++ to avoid interleaving of output of shell services with the status output on the console. Note that this ++ type is useful only to improve console output, it is not useful as a general unit ordering tool, and the ++ effect of this service type is subject to a 5s time-out, after which the service program is invoked ++ anyway. ++ ++ ++ It is generally recommended to use Type= for long-running ++ services whenever possible, as it is the simplest and fastest option. However, as this service type won't ++ propagate service start-up failures and doesn't allow ordering of other units against completion of ++ initialization of the service (which for example is useful if clients need to connect to the service through ++ some form of IPC, and the IPC channel is only established by the service itself — in contrast to doing this ++ ahead of time through socket or bus activation or similar), it might not be sufficient for many cases. If so, ++ or (the latter only in case the service provides a D-Bus ++ interface) are the preferred options as they allow service program code to precisely schedule when to ++ consider the service started up successfully and when to proceed with follow-up units. The ++ service type requires explicit support in the service codebase (as ++ sd_notify() or an equivalent API needs to be invoked by the service at the appropriate ++ time) — if it's not supported, then is an alternative: it supports the traditional ++ UNIX service start-up protocol. Finally, might be an option for cases where it is ++ enough to ensure the service binary is invoked, and where the service binary itself executes no or little ++ initialization on its own (and its initialization is unlikely to fail). Note that using any type other than ++ possibly delays the boot process, as the service manager needs to wait for service ++ initialization to complete. It is hence recommended not to needlessly use any types other than ++ . (Also note it is generally not recommended to use or ++ for long-running services.) + + + diff --git a/SOURCES/0195-sd-bus-allow-connecting-to-the-pseudo-container-.hos.patch b/SOURCES/0195-sd-bus-allow-connecting-to-the-pseudo-container-.hos.patch new file mode 100644 index 0000000..2bad492 --- /dev/null +++ b/SOURCES/0195-sd-bus-allow-connecting-to-the-pseudo-container-.hos.patch @@ -0,0 +1,57 @@ +From 6bf178a9a6e3fd6544d8e37a0082febf81c0ad2d Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 17 Jul 2018 12:23:26 +0200 +Subject: [PATCH] sd-bus: allow connecting to the pseudo-container ".host" + +machined exposes the pseudo-container ".host" as a reference to the host +system, and this means "machinectl login .host" and "machinectl shell +.host" get your a login/shell on the host. systemd-run currently doesn't +allow that. Let's fix that, and make sd-bus understand ".host" as an +alias for connecting to the host system. + +(cherry picked from commit 1e5057b904473696ae0d591d7555233adcb51fa4) + +Resolves: #1683334 +--- + src/basic/util.c | 5 +++++ + src/libsystemd/sd-bus/sd-bus.c | 4 ++-- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/basic/util.c b/src/basic/util.c +index 8f2d6061da..82cb937314 100644 +--- a/src/basic/util.c ++++ b/src/basic/util.c +@@ -255,6 +255,11 @@ int container_get_leader(const char *machine, pid_t *pid) { + assert(machine); + assert(pid); + ++ if (streq(machine, ".host")) { ++ *pid = 1; ++ return 0; ++ } ++ + if (!machine_name_is_valid(machine)) + return -EINVAL; + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 7f03528b89..f53a98d6bf 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -952,7 +952,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) + return -EINVAL; + + if (machine) { +- if (!machine_name_is_valid(machine)) ++ if (!streq(machine, ".host") && !machine_name_is_valid(machine)) + return -EINVAL; + + free_and_replace(b->machine, machine); +@@ -1450,7 +1450,7 @@ _public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); +- assert_return(machine_name_is_valid(machine), -EINVAL); ++ assert_return(streq(machine, ".host") || machine_name_is_valid(machine), -EINVAL); + + r = sd_bus_new(&b); + if (r < 0) diff --git a/SOURCES/0196-sd-login-let-s-also-make-sd-login-understand-.host.patch b/SOURCES/0196-sd-login-let-s-also-make-sd-login-understand-.host.patch new file mode 100644 index 0000000..08b90d7 --- /dev/null +++ b/SOURCES/0196-sd-login-let-s-also-make-sd-login-understand-.host.patch @@ -0,0 +1,56 @@ +From 1fd670e06332423a3e0b19ca717145c14e8418a1 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 17 Jul 2018 12:24:50 +0200 +Subject: [PATCH] sd-login: let's also make sd-login understand ".host" + +if sd-bus and machined grok it, then sd-login should grok it too. + +(cherry picked from commit a8c9b7a0fc0aa02666042543ff9a652aae3c9499) + +Resolves: #1683334 +--- + src/libsystemd/sd-login/sd-login.c | 27 +++++++++++++++++---------- + 1 file changed, 17 insertions(+), 10 deletions(-) + +diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c +index c2f7133e42..aeae6d78a9 100644 +--- a/src/libsystemd/sd-login/sd-login.c ++++ b/src/libsystemd/sd-login/sd-login.c +@@ -892,20 +892,27 @@ _public_ int sd_machine_get_class(const char *machine, char **class) { + const char *p; + int r; + +- assert_return(machine_name_is_valid(machine), -EINVAL); + assert_return(class, -EINVAL); + +- p = strjoina("/run/systemd/machines/", machine); +- r = parse_env_file(NULL, p, NEWLINE, "CLASS", &c, NULL); +- if (r == -ENOENT) +- return -ENXIO; +- if (r < 0) +- return r; +- if (!c) +- return -EIO; ++ if (streq(machine, ".host")) { ++ c = strdup("host"); ++ if (!c) ++ return -ENOMEM; ++ } else { ++ if (!machine_name_is_valid(machine)) ++ return -EINVAL; + +- *class = TAKE_PTR(c); ++ p = strjoina("/run/systemd/machines/", machine); ++ r = parse_env_file(NULL, p, NEWLINE, "CLASS", &c, NULL); ++ if (r == -ENOENT) ++ return -ENXIO; ++ if (r < 0) ++ return r; ++ if (!c) ++ return -EIO; ++ } + ++ *class = TAKE_PTR(c); + return 0; + } + diff --git a/SOURCES/0197-test-add-test-for-Type-exec.patch b/SOURCES/0197-test-add-test-for-Type-exec.patch new file mode 100644 index 0000000..f1a2c90 --- /dev/null +++ b/SOURCES/0197-test-add-test-for-Type-exec.patch @@ -0,0 +1,109 @@ +From afb82e108fe45d8481f2be50c74c4c6f307f8d60 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 17 Jul 2018 12:35:12 +0200 +Subject: [PATCH] test: add test for Type=exec + +(cherry picked from commit 0e1f17561f5f6061ec5503de044298372ed7ca37) + +Resolves: #1683334 +--- + test/TEST-23-TYPE-EXEC/Makefile | 4 +++ + test/TEST-23-TYPE-EXEC/test.sh | 42 +++++++++++++++++++++++++++++ + test/TEST-23-TYPE-EXEC/testsuite.sh | 28 +++++++++++++++++++ + 3 files changed, 74 insertions(+) + create mode 100644 test/TEST-23-TYPE-EXEC/Makefile + create mode 100755 test/TEST-23-TYPE-EXEC/test.sh + create mode 100755 test/TEST-23-TYPE-EXEC/testsuite.sh + +diff --git a/test/TEST-23-TYPE-EXEC/Makefile b/test/TEST-23-TYPE-EXEC/Makefile +new file mode 100644 +index 0000000000..34d7cc6cdf +--- /dev/null ++++ b/test/TEST-23-TYPE-EXEC/Makefile +@@ -0,0 +1,4 @@ ++BUILD_DIR=$(shell ../../tools/find-build-dir.sh) ++ ++all setup clean run: ++ @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-23-TYPE-EXEC/test.sh b/test/TEST-23-TYPE-EXEC/test.sh +new file mode 100755 +index 0000000000..bdcea239a7 +--- /dev/null ++++ b/test/TEST-23-TYPE-EXEC/test.sh +@@ -0,0 +1,42 @@ ++#!/bin/bash ++# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- ++# ex: ts=8 sw=4 sts=4 et filetype=sh ++set -e ++TEST_DESCRIPTION="test Type=exec" ++ ++. $TEST_BASE_DIR/test-functions ++ ++test_setup() { ++ create_empty_image ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ ++ ( ++ LOG_LEVEL=5 ++ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) ++ ++ setup_basic_environment ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service < /testok ++ ++exit 0 diff --git a/SOURCES/0198-journal-gateway-explicitly-declare-local-variables.patch b/SOURCES/0198-journal-gateway-explicitly-declare-local-variables.patch new file mode 100644 index 0000000..c6ebb61 --- /dev/null +++ b/SOURCES/0198-journal-gateway-explicitly-declare-local-variables.patch @@ -0,0 +1,75 @@ +From e020d3e72755fc8c25d33efad1b7322e42d8610b Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 27 Jun 2018 14:22:24 +0900 +Subject: [PATCH] journal-gateway: explicitly declare local variables + +Suggested by LGTM. + +(cherry picked from commit c497e449f41774a36e01ae2cc2abade6133dffe1) + +Resolves: #1705971 +--- + src/journal-remote/browse.html | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/journal-remote/browse.html b/src/journal-remote/browse.html +index 32848c7673..9f519adbd6 100644 +--- a/src/journal-remote/browse.html ++++ b/src/journal-remote/browse.html +@@ -304,7 +304,6 @@ + var buf = ''; + + for (i in l) { +- + if (l[i] == '') + continue; + +@@ -322,6 +321,7 @@ + else + priority = 6; + ++ var clazz; + if (priority <= 3) + clazz = "message-error"; + else if (priority <= 5) +@@ -388,7 +388,7 @@ + var d = JSON.parse(event.currentTarget.responseText); + + document.getElementById("diventry").style.display = "block"; +- entry = document.getElementById("tableentry"); ++ var entry = document.getElementById("tableentry"); + + var buf = ""; + for (var key in d) { +@@ -455,7 +455,7 @@ + (event.currentTarget.status != 200 && event.currentTarget.status != 0)) + return; + +- f = document.getElementById("filter"); ++ var f = document.getElementById("filter"); + + var l = event.currentTarget.responseText.split('\n'); + var buf = ''; +@@ -511,11 +511,12 @@ + } + + function initFilter() { +- f = document.getElementById("filter"); ++ var f = document.getElementById("filter"); + + var buf = ''; + + var filter = localStorage["filter"]; ++ var j; + if (filter != null && filter != "") { + buf += ''; + j = 1; +@@ -529,7 +530,7 @@ + function installHandlers() { + document.onkeyup = onKeyUp; + +- logs = document.getElementById("divlogs"); ++ var logs = document.getElementById("divlogs"); + logs.addEventListener("mousewheel", onMouseWheel, false); + logs.addEventListener("DOMMouseScroll", onMouseWheel, false); + } diff --git a/SOURCES/0199-tools-drop-unused-variable.patch b/SOURCES/0199-tools-drop-unused-variable.patch new file mode 100644 index 0000000..457a2a2 --- /dev/null +++ b/SOURCES/0199-tools-drop-unused-variable.patch @@ -0,0 +1,24 @@ +From b7786387a6cdc447b874f36a8d9831b1acd2eb13 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Sat, 8 Dec 2018 19:33:37 +0900 +Subject: [PATCH] tools: drop unused variable + +(cherry picked from commit 2f6c9b6f3fb0128cee7f74985c143b4850feff6d) + +Resolves: #1705971 +--- + tools/gdb-sd_dump_hashmaps.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tools/gdb-sd_dump_hashmaps.py b/tools/gdb-sd_dump_hashmaps.py +index ea15160107..0701d139e2 100644 +--- a/tools/gdb-sd_dump_hashmaps.py ++++ b/tools/gdb-sd_dump_hashmaps.py +@@ -16,7 +16,6 @@ class sd_dump_hashmaps(gdb.Command): + d = gdb.parse_and_eval("hashmap_debug_list") + all_entry_sizes = gdb.parse_and_eval("all_entry_sizes") + all_direct_buckets = gdb.parse_and_eval("all_direct_buckets") +- hashmap_base_t = gdb.lookup_type("HashmapBase") + uchar_t = gdb.lookup_type("unsigned char") + ulong_t = gdb.lookup_type("unsigned long") + debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug") diff --git a/SOURCES/0200-journal-gateway-use-localStorage-cursor-only-when-it.patch b/SOURCES/0200-journal-gateway-use-localStorage-cursor-only-when-it.patch new file mode 100644 index 0000000..7dbc0c3 --- /dev/null +++ b/SOURCES/0200-journal-gateway-use-localStorage-cursor-only-when-it.patch @@ -0,0 +1,36 @@ +From 8ae19bfcfe0a52b55171f4f60772afe418831230 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 27 Jun 2018 14:50:19 +0900 +Subject: [PATCH] journal-gateway: use localStorage["cursor"] only when it has + valid value + +Discovered by LGTM. + +(cherry picked from commit 944072feddb73333023d0a98bf87fd2a17f894d3) + +Resolves: #1705971 +--- + src/journal-remote/browse.html | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/journal-remote/browse.html b/src/journal-remote/browse.html +index 9f519adbd6..e5162d1088 100644 +--- a/src/journal-remote/browse.html ++++ b/src/journal-remote/browse.html +@@ -236,10 +236,12 @@ + + function entriesLoad(range) { + +- if (range == null) +- range = localStorage["cursor"] + ":0"; +- if (range == null) +- range = ""; ++ if (range == null) { ++ if (localStorage["cursor"] != null && localStorage["cursor"] != "") ++ range = localStorage["cursor"] + ":0"; ++ else ++ range = ""; ++ } + + var url = "/entries"; + diff --git a/SOURCES/0201-sd-bus-deal-with-cookie-overruns.patch b/SOURCES/0201-sd-bus-deal-with-cookie-overruns.patch new file mode 100644 index 0000000..bf61bb4 --- /dev/null +++ b/SOURCES/0201-sd-bus-deal-with-cookie-overruns.patch @@ -0,0 +1,87 @@ +From 980418c331293aeb8595fcc95cbc4a9e1a485eda Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 25 Feb 2019 11:02:46 +0100 +Subject: [PATCH] sd-bus: deal with cookie overruns + +Apparently this happens IRL. Let's carefully deal with issues like this: +when we overrun, let's not go back to zero but instead leave the highest +cookie bit set. We use that as indication that we are in "overrun +territory", and then are particularly careful with checking cookies, +i.e. that they haven't been used for still outstanding replies yet. This +should retain the quick cookie generation behaviour we used to have, but +permits dealing with overruns. + +Replaces: #11804 +Fixes: #11809 +(cherry picked from commit 1f82f5bb4237ed5f015daf93f818e9db95e764b8) + +Resolves: #1694999 +--- + src/libsystemd/sd-bus/sd-bus.c | 47 +++++++++++++++++++++++++++++++++- + 1 file changed, 46 insertions(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index f53a98d6bf..3583e24e64 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -1597,6 +1597,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; + +@@ -1620,7 +1661,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) { diff --git a/SOURCES/0202-journal-remote-do-not-request-Content-Length-if-Tran.patch b/SOURCES/0202-journal-remote-do-not-request-Content-Length-if-Tran.patch new file mode 100644 index 0000000..431cdff --- /dev/null +++ b/SOURCES/0202-journal-remote-do-not-request-Content-Length-if-Tran.patch @@ -0,0 +1,77 @@ +From f551c05e4799386508e10f0f007251e426493a67 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 11 Mar 2019 12:27:18 +0900 +Subject: [PATCH] journal-remote: do not request Content-Length if + Transfer-Encoding is chunked + +This fixes a bug introduced by 7fdb237f5473cb8fc2129e57e8a0039526dcb4fd. + +Closes #11571. + +(cherry picked from commit a289dfd69b3ff4bccdde93e84b67c947bafa27e1) + +Resolves: #1708849 +--- + src/journal-remote/journal-remote-main.c | 41 ++++++++++++++++-------- + 1 file changed, 27 insertions(+), 14 deletions(-) + +diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c +index 5b0bbba310..47fe9d7433 100644 +--- a/src/journal-remote/journal-remote-main.c ++++ b/src/journal-remote/journal-remote-main.c +@@ -254,6 +254,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); +@@ -279,21 +280,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, "Transfer-Encoding"); ++ if (header) { ++ if (!strcaseeq(header, "chunked")) ++ return mhd_respondf(connection, 0, MHD_HTTP_BAD_REQUEST, ++ "Unsupported Transfer-Encoding type: %s", header); ++ ++ chunked = true; ++ } ++ + 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); +- +- 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); ++ 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/SOURCES/0203-journal-do-not-remove-multiple-spaces-after-identifi.patch b/SOURCES/0203-journal-do-not-remove-multiple-spaces-after-identifi.patch new file mode 100644 index 0000000..3c7d150 --- /dev/null +++ b/SOURCES/0203-journal-do-not-remove-multiple-spaces-after-identifi.patch @@ -0,0 +1,79 @@ +From fffbf1f90be5236b310bc0b10034815b1051f0ac Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 10 Aug 2018 11:07:54 +0900 +Subject: [PATCH] journal: do not remove multiple spaces after identifier in + syslog message + +Single space is used as separator. +C.f. discussions in #156. + +Fixes #9839 introduced by a6aadf4ae0bae185dc4c414d492a4a781c80ffe5. + +(cherry picked from commit 8595102d3ddde6d25c282f965573a6de34ab4421) + +Resolves: #1691817 +--- + src/journal/journald-syslog.c | 4 +++- + src/journal/test-journal-syslog.c | 24 ++++++++++++++---------- + 2 files changed, 17 insertions(+), 11 deletions(-) + +diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c +index 97711ac7a3..e0b55cc566 100644 +--- a/src/journal/journald-syslog.c ++++ b/src/journal/journald-syslog.c +@@ -219,7 +219,9 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) + if (t) + *identifier = t; + +- e += strspn(p + e, WHITESPACE); ++ /* Single space is used as separator */ ++ if (p[e] != '\0' && strchr(WHITESPACE, p[e])) ++ e++; + + *buf = p + e; + return e; +diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c +index 05f759817e..7294cde032 100644 +--- a/src/journal/test-journal-syslog.c ++++ b/src/journal/test-journal-syslog.c +@@ -6,7 +6,7 @@ + #include "string-util.h" + + static void test_syslog_parse_identifier(const char *str, +- const char *ident, const char *pid, int ret) { ++ const char *ident, const char *pid, const char *rest, int ret) { + const char *buf = str; + _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; + int ret2; +@@ -16,18 +16,22 @@ static void test_syslog_parse_identifier(const char *str, + assert_se(ret == ret2); + assert_se(ident == ident2 || streq_ptr(ident, ident2)); + assert_se(pid == pid2 || streq_ptr(pid, pid2)); ++ assert_se(streq(buf, rest)); + } + + int main(void) { +- test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11); +- test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6); +- test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 7); +- test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0); +- test_syslog_parse_identifier(":", "", NULL, 1); +- test_syslog_parse_identifier(": ", "", NULL, 3); +- test_syslog_parse_identifier("pidu:", "pidu", NULL, 5); +- test_syslog_parse_identifier("pidu: ", "pidu", NULL, 6); +- test_syslog_parse_identifier("pidu : ", NULL, NULL, 0); ++ test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", "xxx", 11); ++ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, "xxx", 6); ++ test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, " xxx", 6); ++ test_syslog_parse_identifier("pidu xxx", NULL, NULL, "pidu xxx", 0); ++ test_syslog_parse_identifier(" pidu xxx", NULL, NULL, " pidu xxx", 0); ++ test_syslog_parse_identifier("", NULL, NULL, "", 0); ++ test_syslog_parse_identifier(" ", NULL, NULL, " ", 0); ++ test_syslog_parse_identifier(":", "", NULL, "", 1); ++ test_syslog_parse_identifier(": ", "", NULL, " ", 2); ++ test_syslog_parse_identifier("pidu:", "pidu", NULL, "", 5); ++ test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6); ++ test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0); + + return 0; + } diff --git a/SOURCES/0204-cryptsetup-Do-not-fallback-to-PLAIN-mapping-if-LUKS-.patch b/SOURCES/0204-cryptsetup-Do-not-fallback-to-PLAIN-mapping-if-LUKS-.patch new file mode 100644 index 0000000..eab6715 --- /dev/null +++ b/SOURCES/0204-cryptsetup-Do-not-fallback-to-PLAIN-mapping-if-LUKS-.patch @@ -0,0 +1,62 @@ +From 4f9d00380ea41f5a4eb1610ae5c354a8f749cc98 Mon Sep 17 00:00:00 2001 +From: Milan Broz +Date: Mon, 27 May 2019 09:27:54 +0200 +Subject: [PATCH] cryptsetup: Do not fallback to PLAIN mapping if LUKS data + device set fails. + +If crypt_load() for LUKS succeeds, we know that it is a LUKS device. +Failure of data device setting should fail in this case; remapping +as a PLAIN device late could mean data corruption. + +(If a user wants to map PLAIN device over a device with LUKS header, +it should be said explicitly with "plain" argument type.) + +Also, if there is no explicit PLAIN type requested and crypt device +is already initialized (crypt_data_type() is set), do not run +the initialization again. + +(cherry picked from commit 2e4beb875bcb24e7d7d4339cc202b0b3f2953f71) + +Related: #1719153 +--- + src/cryptsetup/cryptsetup.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index abeba44ee8..5be1469d69 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -492,11 +492,14 @@ static int attach_luks_or_plain(struct crypt_device *cd, + return r; + } + +- if (data_device) ++ if (data_device) { + r = crypt_set_data_device(cd, data_device); ++ if (r < 0) ++ return log_error_errno(r, "Failed to set LUKS data device %s: %m", data_device); ++ } + } + +- if ((!arg_type && r < 0) || streq_ptr(arg_type, CRYPT_PLAIN)) { ++ if ((!arg_type && !crypt_get_type(cd)) || streq_ptr(arg_type, CRYPT_PLAIN)) { + struct crypt_params_plain params = { + .offset = arg_offset, + .skip = arg_skip, +@@ -543,14 +546,13 @@ static int attach_luks_or_plain(struct crypt_device *cd, + * parameters when used for plain + * mode. */ + r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, ¶ms); ++ if (r < 0) ++ return log_error_errno(r, "Loading of cryptographic parameters failed: %m"); + + /* hash == NULL implies the user passed "plain" */ + pass_volume_key = (params.hash == NULL); + } + +- if (r < 0) +- return log_error_errno(r, "Loading of cryptographic parameters failed: %m"); +- + log_info("Set cipher %s, mode %s, key size %i bits for device %s.", + crypt_get_cipher(cd), + crypt_get_cipher_mode(cd), diff --git a/SOURCES/0205-cryptsetup-call-crypt_load-for-LUKS-only-once.patch b/SOURCES/0205-cryptsetup-call-crypt_load-for-LUKS-only-once.patch new file mode 100644 index 0000000..543efeb --- /dev/null +++ b/SOURCES/0205-cryptsetup-call-crypt_load-for-LUKS-only-once.patch @@ -0,0 +1,79 @@ +From 788fb775f7deb8c456868362454e2a5f50c6068f Mon Sep 17 00:00:00 2001 +From: Milan Broz +Date: Mon, 27 May 2019 09:43:03 +0200 +Subject: [PATCH] cryptsetup: call crypt_load() for LUKS only once + +The crypt_load() for LUKS2 can read a quite big area of disk +(metadata area size is configurable and can increase up to megabytes). + +This initialization is not needed to be repeated, just use the existing context. + +(This patch is also required for the following change.) + +(cherry picked from commit ea9a9d49e4af31c49e5c216e7e5e2f533e727579) + +Related: #1719153 +--- + src/cryptsetup/cryptsetup.c | 28 ++++++++++++---------------- + 1 file changed, 12 insertions(+), 16 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 5be1469d69..a0bd80ea65 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -475,7 +475,6 @@ static int attach_tcrypt( + static int attach_luks_or_plain(struct crypt_device *cd, + const char *name, + const char *key_file, +- const char *data_device, + char **passwords, + uint32_t flags) { + int r = 0; +@@ -485,20 +484,6 @@ static int attach_luks_or_plain(struct crypt_device *cd, + assert(name); + assert(key_file || passwords); + +- if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) { +- r = crypt_load(cd, CRYPT_LUKS, NULL); +- if (r < 0) { +- log_error("crypt_load() failed on device %s.\n", crypt_get_device_name(cd)); +- return r; +- } +- +- if (data_device) { +- r = crypt_set_data_device(cd, data_device); +- if (r < 0) +- return log_error_errno(r, "Failed to set LUKS data device %s: %m", data_device); +- } +- } +- + if ((!arg_type && !crypt_get_type(cd)) || streq_ptr(arg_type, CRYPT_PLAIN)) { + struct crypt_params_plain params = { + .offset = arg_offset, +@@ -687,6 +672,18 @@ int main(int argc, char *argv[]) { + log_warning("Key file %s is world-readable. This is not a good idea!", key_file); + } + ++ if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) { ++ r = crypt_load(cd, CRYPT_LUKS, NULL); ++ if (r < 0) ++ return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd)); ++ ++ if (arg_header) { ++ r = crypt_set_data_device(cd, argv[3]); ++ if (r < 0) ++ return log_error_errno(r, "Failed to set LUKS data device %s: %m", argv[3]); ++ } ++ } ++ + for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { + _cleanup_strv_free_erase_ char **passwords = NULL; + +@@ -704,7 +701,6 @@ int main(int argc, char *argv[]) { + r = attach_luks_or_plain(cd, + argv[2], + key_file, +- arg_header ? argv[3] : NULL, + passwords, + flags); + if (r >= 0) diff --git a/SOURCES/0206-cryptsetup-Add-LUKS2-token-support.patch b/SOURCES/0206-cryptsetup-Add-LUKS2-token-support.patch new file mode 100644 index 0000000..f818181 --- /dev/null +++ b/SOURCES/0206-cryptsetup-Add-LUKS2-token-support.patch @@ -0,0 +1,45 @@ +From 7a597a091de83a861d81166b0e863bf2977c829c Mon Sep 17 00:00:00 2001 +From: Milan Broz +Date: Mon, 27 May 2019 09:44:14 +0200 +Subject: [PATCH] cryptsetup: Add LUKS2 token support. + +LUKS2 supports so-called tokens. The libcryptsetup internally +support keyring token (it tries to open device using specified +keyring entry). +Only if all token fails (or are not available), it uses a passphrase. + +This patch aligns the functionality with the cryptsetup utility +(cryptsetup luksOpen tries tokens first) but does not replace +the systemd native ask-password function (can be used the same in +combination with this patch). + +(cherry picked from commit 894bb3ca4c730cc9e9d46ef5004ba4ca5e201d8d) + +Resolves: #1719153 +--- + src/cryptsetup/cryptsetup.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index a0bd80ea65..4e1b3eff19 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -682,6 +682,18 @@ int main(int argc, char *argv[]) { + if (r < 0) + return log_error_errno(r, "Failed to set LUKS data device %s: %m", argv[3]); + } ++#ifdef CRYPT_ANY_TOKEN ++ /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */ ++ if (!key_file) { ++ r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags); ++ if (r >= 0) { ++ log_debug("Volume %s activated with LUKS token id %i.", argv[2], r); ++ return 0; ++ } ++ ++ log_debug_errno(r, "Token activation unsuccessful for device %s: %m", crypt_get_device_name(cd)); ++ } ++#endif + } + + for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) { diff --git a/SOURCES/0207-udev-scsi_id-fix-incorrect-page-length-when-get-devi.patch b/SOURCES/0207-udev-scsi_id-fix-incorrect-page-length-when-get-devi.patch new file mode 100644 index 0000000..a884b4f --- /dev/null +++ b/SOURCES/0207-udev-scsi_id-fix-incorrect-page-length-when-get-devi.patch @@ -0,0 +1,30 @@ +From 7b3ef169e3142fb471c48f265881b371380d77e0 Mon Sep 17 00:00:00 2001 +From: Zhang Xianwei +Date: Mon, 13 May 2019 18:41:55 +0800 +Subject: [PATCH] udev/scsi_id: fix incorrect page length when get device + identification VPD page + +The length of device identification VPD page is filled with two bytes, +but scsi_id only gets the low byte. Fix it. + +Signed-off-by: Zhang Xianwei +(cherry picked from commit 1f7b6872dbe8ccae1f3bda9aa6aeb87c9b42e01e) + +Resolves: #1713227 +--- + src/udev/scsi_id/scsi_serial.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c +index fd91657a32..fb6055395d 100644 +--- a/src/udev/scsi_id/scsi_serial.c ++++ b/src/udev/scsi_id/scsi_serial.c +@@ -661,7 +661,7 @@ static int do_scsi_page83_inquiry(struct udev *udev, + * Examine each descriptor returned. There is normally only + * one or a small number of descriptors. + */ +- for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) { ++ for (j = 4; j <= ((unsigned)page_83[2] << 8) + (unsigned)page_83[3] + 3; j += page_83[j + 3] + 4) { + retval = check_fill_0x83_id(udev, + dev_scsi, &page_83[j], + &id_search_list[id_ind], diff --git a/SOURCES/0208-Change-job-mode-of-manager-triggered-restarts-to-JOB.patch b/SOURCES/0208-Change-job-mode-of-manager-triggered-restarts-to-JOB.patch new file mode 100644 index 0000000..c40fd1c --- /dev/null +++ b/SOURCES/0208-Change-job-mode-of-manager-triggered-restarts-to-JOB.patch @@ -0,0 +1,55 @@ +From d70e1c2eb596b8144197192e2324abbb45f547a6 Mon Sep 17 00:00:00 2001 +From: Jonathon Kowalski +Date: Thu, 17 Jan 2019 17:08:00 +0000 +Subject: [PATCH] Change job mode of manager triggered restarts to JOB_REPLACE + +Fixes: #11305 +Fixes: #3260 +Related: #11456 + +So, here's what happens in the described scenario in #11305. A unit goes +down, and that triggeres stop jobs for the other two units as they were +bound to it. Now, the timer for manager triggered restarts kicks in and +schedules a restart job with the JOB_FAIL job mode. This means there is +a stop job installed on those units, and now due to them being bound to +us they also get a restart job enqueued. This however is a conflicts, as +neither stop can merge into restart, nor restart into stop. However, +restart should be able to replace stop in any case. If the stop +procedure is ongoing, it can cancel the stop job, install itself, and +then after reaching dead finish and convert itself to a start job. +However, if we increase the timer, then it can always take those units +from inactive -> auto-restart. + +We change the job mode to JOB_REPLACE so the restart job cancels the +stop job and installs itself. + +Also, the original bug could be worked around by bumping RestartSec= to +avoid the conflicting. + +This doesn't seem to be something that is going to break uses. That is +because for those who already had it working, there must have never been +conflicting jobs, as that would result in a desctructive transaction by +virtue of the job mode used. + +After this change, the test case is able to work nicely without issues. + +(cherry picked from commit 03ff2dc71ecb09272d728d458498b44f7f132f51) + +Resolves: #1712524 +--- + src/core/service.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 3eab749362..8342c131c8 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2133,7 +2133,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_FAIL, &error, NULL); ++ r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_REPLACE, &error, NULL); + if (r < 0) + goto fail; + diff --git a/SOURCES/0209-bash-completion-analyze-support-security.patch b/SOURCES/0209-bash-completion-analyze-support-security.patch new file mode 100644 index 0000000..aec8a8d --- /dev/null +++ b/SOURCES/0209-bash-completion-analyze-support-security.patch @@ -0,0 +1,58 @@ +From 8d1a8f099dbf79d0e18e055721228192a637a759 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 6 Dec 2018 18:51:56 +0100 +Subject: [PATCH] bash-completion: analyze: support 'security' + +(cherry picked from commit 83da42c3bf86e8787cfec2c7fb6ca379dfec3632) + +Resolves: #1733395 +--- + shell-completion/bash/systemd-analyze | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze +index 21d0fcf1b8..b4fcfc6492 100644 +--- a/shell-completion/bash/systemd-analyze ++++ b/shell-completion/bash/systemd-analyze +@@ -31,8 +31,13 @@ __get_machines() { + machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; }; + } + ++__get_services() { ++ systemctl list-units --no-legend --no-pager -t service --all $1 | \ ++ { while read -r a b c; do [[ $b == "loaded" ]]; echo " $a"; done } ++} ++ + _systemd_analyze() { +- local i verb comps ++ local i verb comps mode + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + + local -A OPTS=( +@@ -51,6 +56,7 @@ _systemd_analyze() { + [SECCOMP_FILTER]='syscall-filter' + [SERVICE_WATCHDOGS]='service-watchdogs' + [CAT_CONFIG]='cat-config' ++ [SECURITY]='security' + ) + + local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf +@@ -149,6 +155,18 @@ _systemd_analyze() { + comps="$CONFIGS $( compgen -A file -- "$cur" )" + compopt -o filenames + fi ++ ++ elif __contains_word "$verb" ${VERBS[SECURITY]}; then ++ if [[ $cur = -* ]]; then ++ comps='--help --version --no-pager --system --user -H --host -M --machine' ++ else ++ if __contains_word "--user" ${COMP_WORDS[*]}; then ++ mode=--user ++ else ++ mode=--system ++ fi ++ comps=$( __get_services $mode ) ++ fi + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) diff --git a/SOURCES/0210-man-note-that-journal-does-not-validate-syslog-field.patch b/SOURCES/0210-man-note-that-journal-does-not-validate-syslog-field.patch new file mode 100644 index 0000000..3e9de97 --- /dev/null +++ b/SOURCES/0210-man-note-that-journal-does-not-validate-syslog-field.patch @@ -0,0 +1,28 @@ +From 705a67a53a8a1b836ef17f048366bbf33357afc1 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 14 May 2019 10:45:08 +0200 +Subject: [PATCH] man: note that journal does not validate syslog fields + +(cherry picked from commit 63ea8032f28052f7cda860e5324c0a83dee7ed23) + +Resolves: #1707175 +--- + man/systemd.journal-fields.xml | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml +index c079274c32..0c95c4cd95 100644 +--- a/man/systemd.journal-fields.xml ++++ b/man/systemd.journal-fields.xml +@@ -111,6 +111,11 @@ + program_invocation_short_name variable, + see + program_invocation_short_name3.) ++ Note that the journal service does not validate the values of any structured ++ journal fields whose name is not prefixed with an underscore, and this includes any ++ syslog related fields such as these. Hence, applications that supply a facility, PID, ++ or log level are expected to do so properly formatted, i.e. as numeric integers formatted ++ as decimal strings. + + + diff --git a/SOURCES/0211-rules-skip-memory-hotplug-on-ppc64.patch b/SOURCES/0211-rules-skip-memory-hotplug-on-ppc64.patch new file mode 100644 index 0000000..3e311ee --- /dev/null +++ b/SOURCES/0211-rules-skip-memory-hotplug-on-ppc64.patch @@ -0,0 +1,22 @@ +From 72dd8d8cd1a7417805009050f859d502b1c6cf3e Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Thu, 6 Jun 2019 09:35:27 +0200 +Subject: [PATCH] rules: skip memory hotplug on ppc64 + +Resolves (#1713159) +--- + rules/40-redhat.rules | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index 17b33682bd..fadc6e59f1 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -7,6 +7,7 @@ SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online} + SUBSYSTEM!="memory", GOTO="memory_hotplug_end" + ACTION!="add", GOTO="memory_hotplug_end" + PROGRAM="/bin/uname -p", RESULT=="s390*", GOTO="memory_hotplug_end" ++PROGRAM="/bin/uname -p", RESULT=="ppc64*", GOTO="memory_hotplug_end" + + ENV{.state}="online" + PROGRAM="/bin/systemd-detect-virt", RESULT=="none", ENV{.state}="online_movable" diff --git a/SOURCES/0212-mount-simplify-proc-self-mountinfo-handler.patch b/SOURCES/0212-mount-simplify-proc-self-mountinfo-handler.patch new file mode 100644 index 0000000..316dda2 --- /dev/null +++ b/SOURCES/0212-mount-simplify-proc-self-mountinfo-handler.patch @@ -0,0 +1,86 @@ +From daf63a3c6c6cd241017bdf9a26c7b1caf744e69b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 17 Jul 2019 14:53:07 +0200 +Subject: [PATCH] mount: simplify /proc/self/mountinfo handler + +Our IO handler is only installed for one fd, hence there's no reason to +conditionalize on it again. + +Also, split out the draining into a helper function of its own. + +(cherry picked from commit fcd8e119c28be19ffbc5227089cf4d3b8ba60238) + +Conflicts: + src/core/mount.c + +Related: #1696178 +--- + src/core/mount.c | 48 ++++++++++++++++++++++++++---------------------- + 1 file changed, 26 insertions(+), 22 deletions(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 16229d4af1..85b07375e2 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1758,6 +1758,29 @@ fail: + mount_shutdown(m); + } + ++static int drain_libmount(Manager *m) { ++ bool rescan = false; ++ int r; ++ ++ assert(m); ++ ++ /* Drain all events and verify that the event is valid. ++ * ++ * Note that libmount also monitors /run/mount mkdir if the directory does not exist yet. The mkdir ++ * may generate event which is irrelevant for us. ++ * ++ * error: r < 0; valid: r == 0, false positive: r == 1 */ ++ do { ++ r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL); ++ if (r < 0) ++ return log_error_errno(r, "Failed to drain libmount events: %m"); ++ if (r == 0) ++ rescan = true; ++ } while (r == 0); ++ ++ return rescan; ++} ++ + static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + _cleanup_set_free_ Set *around = NULL, *gone = NULL; + Manager *m = userdata; +@@ -1769,28 +1792,9 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, + assert(m); + assert(revents & EPOLLIN); + +- if (fd == mnt_monitor_get_fd(m->mount_monitor)) { +- bool rescan = false; +- +- /* Drain all events and verify that the event is valid. +- * +- * Note that libmount also monitors /run/mount mkdir if the +- * directory does not exist yet. The mkdir may generate event +- * which is irrelevant for us. +- * +- * error: r < 0; valid: r == 0, false positive: rc == 1 */ +- do { +- r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL); +- if (r == 0) +- rescan = true; +- else if (r < 0) +- return log_error_errno(r, "Failed to drain libmount events"); +- } while (r == 0); +- +- log_debug("libmount event [rescan: %s]", yes_no(rescan)); +- if (!rescan) +- return 0; +- } ++ r = drain_libmount(m); ++ if (r <= 0) ++ return r; + + r = mount_load_proc_self_mountinfo(m, true); + if (r < 0) { diff --git a/SOURCES/0213-mount-rescan-proc-self-mountinfo-before-processing-w.patch b/SOURCES/0213-mount-rescan-proc-self-mountinfo-before-processing-w.patch new file mode 100644 index 0000000..032ea39 --- /dev/null +++ b/SOURCES/0213-mount-rescan-proc-self-mountinfo-before-processing-w.patch @@ -0,0 +1,90 @@ +From 4bc21bbc61acd1ce114da381a9742f6bcd4ffde8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 17 Jul 2019 18:57:13 +0200 +Subject: [PATCH] mount: rescan /proc/self/mountinfo before processing waitid() + results + +(The interesting bits about the what and why are in a comment in the +patch, please have a look there instead of looking here in the commit +msg). + +Fixes: #10872 +(cherry picked from commit 350804867dbcc9b7ccabae1187d730d37e2d8a21) + +Conflicts: + src/core/mount.c + +Resolves: #1696178 +--- + src/core/mount.c | 30 +++++++++++++++++++++++++++--- + 1 file changed, 27 insertions(+), 3 deletions(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 85b07375e2..2ac04e3874 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -53,6 +53,7 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { + + static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); + static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); ++static int mount_process_proc_self_mountinfo(Manager *m); + + static bool MOUNT_STATE_WITH_PROCESS(MountState state) { + return IN_SET(state, +@@ -1241,6 +1242,22 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { + if (pid != m->control_pid) + return; + ++ /* So here's the thing, we really want to know before /usr/bin/mount or /usr/bin/umount exit whether ++ * they established/remove a mount. This is important when mounting, but even more so when unmounting ++ * since we need to deal with nested mounts and otherwise cannot safely determine whether to repeat ++ * the unmounts. In theory, the kernel fires /proc/self/mountinfo changes off before returning from ++ * the mount() or umount() syscalls, and thus we should see the changes to the proc file before we ++ * process the waitid() for the /usr/bin/(u)mount processes. However, this is unfortunately racy: we ++ * have to waitid() for processes using P_ALL (since we need to reap unexpected children that got ++ * reparented to PID 1), but when using P_ALL we might end up reaping processes that terminated just ++ * instants ago, i.e. already after our last event loop iteration (i.e. after the last point we might ++ * have noticed /proc/self/mountinfo events via epoll). This means event loop priorities for ++ * processing SIGCHLD vs. /proc/self/mountinfo IO events are not as relevant as we want. To fix that ++ * race, let's explicitly scan /proc/self/mountinfo before we start processing /usr/bin/(u)mount ++ * dying. It's ugly, but it makes our ordering systematic again, and makes sure we always see ++ * /proc/self/mountinfo changes before our mount/umount exits. */ ++ (void) mount_process_proc_self_mountinfo(u->manager); ++ + m->control_pid = 0; + + if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) +@@ -1781,16 +1798,14 @@ static int drain_libmount(Manager *m) { + return rescan; + } + +-static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { ++static int mount_process_proc_self_mountinfo(Manager *m) { + _cleanup_set_free_ Set *around = NULL, *gone = NULL; +- Manager *m = userdata; + const char *what; + Iterator i; + Unit *u; + int r; + + assert(m); +- assert(revents & EPOLLIN); + + r = drain_libmount(m); + if (r <= 0) +@@ -1898,6 +1913,15 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, + return 0; + } + ++static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { ++ Manager *m = userdata; ++ ++ assert(m); ++ assert(revents & EPOLLIN); ++ ++ return mount_process_proc_self_mountinfo(m); ++} ++ + static void mount_reset_failed(Unit *u) { + Mount *m = MOUNT(u); + diff --git a/SOURCES/0214-swap-scan-proc-swaps-before-processing-waitid-result.patch b/SOURCES/0214-swap-scan-proc-swaps-before-processing-waitid-result.patch new file mode 100644 index 0000000..e872790 --- /dev/null +++ b/SOURCES/0214-swap-scan-proc-swaps-before-processing-waitid-result.patch @@ -0,0 +1,69 @@ +From a0c135f7771dbe3a6cd3da2aaa106900be0f4470 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 17 Jul 2019 18:58:44 +0200 +Subject: [PATCH] swap: scan /proc/swaps before processing waitid() results + +Similar to the previous commit, but for /proc/swaps, where the same +logic and rationale applies. + +(cherry picked from commit bcce581d65de68cca01c73e1c890e261e72d20af) + +Related: #1696178 +--- + src/core/swap.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/src/core/swap.c b/src/core/swap.c +index e01e61e56d..b644753a1c 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -40,6 +40,7 @@ static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { + + static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); + static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); ++static int swap_process_proc_swaps(Manager *m); + + static bool SWAP_STATE_WITH_PROCESS(SwapState state) { + return IN_SET(state, +@@ -990,6 +991,10 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { + if (pid != s->control_pid) + return; + ++ /* Let's scan /proc/swaps before we process SIGCHLD. For the reasoning see the similar code in ++ * mount.c */ ++ (void) swap_process_proc_swaps(u->manager); ++ + s->control_pid = 0; + + if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL)) +@@ -1125,13 +1130,11 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) { + return r; + } + +-static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { +- Manager *m = userdata; ++static int swap_process_proc_swaps(Manager *m) { + Unit *u; + int r; + + assert(m); +- assert(revents & EPOLLPRI); + + r = swap_load_proc_swaps(m, true); + if (r < 0) { +@@ -1205,6 +1208,15 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v + return 1; + } + ++static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { ++ Manager *m = userdata; ++ ++ assert(m); ++ assert(revents & EPOLLPRI); ++ ++ return swap_process_proc_swaps(m); ++} ++ + static Unit *swap_following(Unit *u) { + Swap *s = SWAP(u); + Swap *other, *first = NULL; diff --git a/SOURCES/0215-analyze-security-fix-potential-division-by-zero.patch b/SOURCES/0215-analyze-security-fix-potential-division-by-zero.patch new file mode 100644 index 0000000..3e150b0 --- /dev/null +++ b/SOURCES/0215-analyze-security-fix-potential-division-by-zero.patch @@ -0,0 +1,25 @@ +From ebe93460ef5ae3744c4b627361f4dc5815cffc13 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 31 Jul 2019 09:13:41 +0200 +Subject: [PATCH] analyze-security: fix potential division by zero + +Upstream PR: https://github.com/systemd/systemd/pull/13238 + +Resolves: #1734400 +--- + src/analyze/analyze-security.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c +index 541fc0d97a..eec040d5c3 100644 +--- a/src/analyze/analyze-security.c ++++ b/src/analyze/analyze-security.c +@@ -1494,6 +1494,8 @@ static int assess(const struct security_info *info, Table *overview_table, Analy + } + } + ++ assert(weight_sum > 0); ++ + if (details_table) { + size_t row; + diff --git a/SOURCES/0216-core-never-propagate-reload-failure-to-service-resul.patch b/SOURCES/0216-core-never-propagate-reload-failure-to-service-resul.patch new file mode 100644 index 0000000..2d967cf --- /dev/null +++ b/SOURCES/0216-core-never-propagate-reload-failure-to-service-resul.patch @@ -0,0 +1,26 @@ +From cffe5d0e781f6fa7f2275b94d2dcc26e00859a78 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 17 Jul 2019 19:16:33 +0200 +Subject: [PATCH] core: never propagate reload failure to service result + +Fixes: #11238 +(cherry picked from commit d611cfa748aaf600832160132774074e808c82c7) + +Resolves: #1735787 +--- + src/core/service.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 8342c131c8..24f167572a 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -3310,7 +3310,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + "Control process exited, code=%s status=%i", + sigchld_code_to_string(code), status); + +- if (s->result == SERVICE_SUCCESS) ++ if (s->state != SERVICE_RELOAD && s->result == SERVICE_SUCCESS) + s->result = f; + + if (s->control_command && diff --git a/SOURCES/0217-man-document-systemd-analyze-security.patch b/SOURCES/0217-man-document-systemd-analyze-security.patch new file mode 100644 index 0000000..07ec674 --- /dev/null +++ b/SOURCES/0217-man-document-systemd-analyze-security.patch @@ -0,0 +1,59 @@ +From d11fdacaf3c804b60dfe8371062f34ac2b624ac9 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Fri, 13 Sep 2019 09:23:32 +0200 +Subject: [PATCH] man: document systemd-analyze security + +(cherry-picked from commit ee93c1e664a7bbc59f1578e285c871999507b14d) + +Resolves: #1750343 +--- + man/systemd-analyze.xml | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml +index 7aa10fc68e..f3b595880f 100644 +--- a/man/systemd-analyze.xml ++++ b/man/systemd-analyze.xml +@@ -106,6 +106,12 @@ + service-watchdogs + BOOL + ++ ++ systemd-analyze ++ OPTIONS ++ security ++ UNIT ++ + + + +@@ -253,6 +259,29 @@ NAutoVTs=8 + systemd.service5. + The hardware watchdog is not affected by this setting. + ++ 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. ++ + If no command is passed, systemd-analyze + time is implied. + diff --git a/SOURCES/0218-man-reorder-and-add-examples-to-systemd-analyze-1.patch b/SOURCES/0218-man-reorder-and-add-examples-to-systemd-analyze-1.patch new file mode 100644 index 0000000..4cfc715 --- /dev/null +++ b/SOURCES/0218-man-reorder-and-add-examples-to-systemd-analyze-1.patch @@ -0,0 +1,772 @@ +From a2e00522971897909db2a81b4daf10e5700f453e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 15 Mar 2019 10:13:55 +0100 +Subject: [PATCH] man: reorder and add examples to systemd-analyze(1) + +The number of verbs supported by systemd-analyze has grown quite a bit, and the +man page has become an unreadable wall of text. Let's put each verb in a +separate subsection, grouping similar verbs together, and add a lot of examples +to guide the user. + +(cherry picked from commit d323a99001c1f7625e8ac902e18deb514a4ca18d) + +Related: #1750343 +--- + man/systemd-analyze.xml | 678 +++++++++++++++++++++++++--------------- + 1 file changed, 429 insertions(+), 249 deletions(-) + +diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml +index f3b595880f..7c873cbdd1 100644 +--- a/man/systemd-analyze.xml ++++ b/man/systemd-analyze.xml +@@ -41,46 +41,50 @@ + critical-chain + UNIT + ++ + + systemd-analyze + OPTIONS +- plot +- > file.svg ++ log-level ++ LEVEL + + + systemd-analyze + OPTIONS +- dot +- PATTERN +- > file.dot ++ log-target ++ TARGET + + + systemd-analyze + OPTIONS +- dump ++ service-watchdogs ++ BOOL + ++ + + systemd-analyze + OPTIONS +- cat-config +- NAME|PATH ++ dump + ++ + + systemd-analyze + OPTIONS +- unit-paths ++ plot ++ >file.svg + + + systemd-analyze + OPTIONS +- log-level +- LEVEL ++ dot ++ PATTERN ++ >file.dot + ++ + + systemd-analyze + OPTIONS +- log-target +- TARGET ++ unit-paths + + + systemd-analyze +@@ -91,20 +95,20 @@ + + systemd-analyze + OPTIONS +- verify +- FILES ++ calendar ++ SPECS + + + systemd-analyze + OPTIONS +- calendar +- SPECS ++ timespan ++ SPAN + + + systemd-analyze + OPTIONS +- service-watchdogs +- BOOL ++ cat-config ++ NAME|PATH + + + systemd-analyze +@@ -123,73 +127,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. +- +- 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. +- +- 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. +- +- systemd-analyze plot prints an SVG +- graphic detailing which system services have been started at what +- time, highlighting the time they spent on initialization. +- +- 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. +- +- 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. +- +- 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). +- ++ If no command is passed, systemd-analyze ++ time is implied. ++ ++ ++ <command>systemd-analyze time</command> ++ ++ 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. ++ ++ ++ <command>Show how long the boot took</command> ++ ++ # in a container ++$ systemd-analyze time ++Startup finished in 296ms (userspace) ++multi-user.target reached after 275ms in userspace ++ ++# 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 ++ ++ ++ ++ ++ ++ <command>systemd-analyze blame</command> ++ ++ 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 + +- +- Showing logind configuration +- $ systemd-analyze cat-config systemd/logind.conf ++ $ 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] +@@ -201,90 +431,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. +- +- 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). +- +- 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). +- +- 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. +- +- 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. +- +- 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. +- +- 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. +- +- 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. ++ ++ ++ + +- If no command is passed, systemd-analyze +- time is implied. ++ ++ <command>systemd-analyze verify <replaceable>FILE</replaceable>...</command> ++ ++ 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. ++ ++ 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. ++ ++ ++ ++ ++ ++ <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 🙂 ++ ++ ++ + + + +@@ -408,88 +670,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/SOURCES/0219-travis-move-to-CentOS-8-docker-images.patch b/SOURCES/0219-travis-move-to-CentOS-8-docker-images.patch new file mode 100644 index 0000000..f5ba536 --- /dev/null +++ b/SOURCES/0219-travis-move-to-CentOS-8-docker-images.patch @@ -0,0 +1,132 @@ +From ac7db0c5b48f1090f77dbcfa0a1e0dc08d5c471e Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 14 Oct 2019 15:26:48 +0200 +Subject: [PATCH] travis: move to CentOS 8 docker images + +As the CentOS 8 Docker images is finally out, we can use it and drop the +plethora of workarounds we had to implement to compile RHEL8 systemd on +CentOS 7. + +Resolves: #1761519 +--- + .travis.yml | 22 ++++++++++------------ + ci/travis-centos-rhel8.sh | 32 +++++++++----------------------- + 2 files changed, 19 insertions(+), 35 deletions(-) + +diff --git a/.travis.yml b/.travis.yml +index 0010da5784..70c60cf24e 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -9,42 +9,40 @@ env: + + jobs: + include: +- - name: CentOS 7 ++ - name: CentOS 8 + language: bash + env: +- - CENTOS_RELEASE="centos7" ++ - CENTOS_RELEASE="centos8" + - CONT_NAME="systemd-centos-$CENTOS_RELEASE" + - DOCKER_EXEC="docker exec -ti $CONT_NAME" + before_install: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version + install: +- - if [ -f meson.build ]; then RHEL_VERSION=rhel8; else RHEL_VERSION=rhel7; fi +- - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh SETUP ++ - $CI_ROOT/travis-centos-rhel8.sh SETUP + script: + - set -e + # Build systemd +- - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh RUN ++ - $CI_ROOT/travis-centos-rhel8.sh RUN + - set +e + after_script: +- - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh CLEANUP ++ - $CI_ROOT/travis-centos-rhel8.sh CLEANUP + +- - name: CentOS 7 (ASan+UBSan) ++ - name: CentOS 8 (ASan+UBSan) + language: bash + env: +- - CENTOS_RELEASE="centos7" ++ - CENTOS_RELEASE="centos8" + - CONT_NAME="systemd-centos-$CENTOS_RELEASE" + - DOCKER_EXEC="docker exec -ti $CONT_NAME" + before_install: + - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce + - docker --version + install: +- - if [ -f meson.build ]; then RHEL_VERSION=rhel8; else RHEL_VERSION=rhel7; fi +- - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh SETUP ++ - $CI_ROOT/travis-centos-rhel8.sh SETUP + script: + - set -e + # Build systemd +- - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh RUN_ASAN ++ - $CI_ROOT/travis-centos-rhel8.sh RUN_ASAN + - set +e + after_script: +- - $CI_ROOT/travis-centos-${RHEL_VERSION}.sh CLEANUP ++ - $CI_ROOT/travis-centos-rhel8.sh CLEANUP +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index c3d1018682..ade44a0413 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -15,10 +15,7 @@ CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" + DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" + DOCKER_RUN="${DOCKER_RUN:-docker run}" + REPO_ROOT="${REPO_ROOT:-$PWD}" +-ADDITIONAL_DEPS=(systemd-ci-environment libidn2-devel python-lxml python36 ninja-build libasan net-tools strace nc busybox e2fsprogs quota dnsmasq) +-# Repo with additional depencencies to compile newer systemd on CentOS 7 +-COPR_REPO="https://copr.fedorainfracloud.org/coprs/mrc0mmand/systemd-centos-ci/repo/epel-7/mrc0mmand-systemd-centos-ci-epel-7.repo" +-COPR_REPO_PATH="/etc/yum.repos.d/${COPR_REPO##*/}" ++ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq) + # RHEL8 options + CONFIGURE_OPTS=( + -Dsysvinit-path=/etc/rc.d/init.d +@@ -95,18 +92,14 @@ for phase in "${PHASES[@]}"; do + -dit --net=host centos:$CENTOS_RELEASE /sbin/init + # Beautiful workaround for Fedora's version of Docker + sleep 1 +- $DOCKER_EXEC yum makecache +- $DOCKER_EXEC curl "$COPR_REPO" -o "$COPR_REPO_PATH" +- $DOCKER_EXEC yum -q -y install epel-release yum-utils +- $DOCKER_EXEC yum-config-manager -q --enable epel +- $DOCKER_EXEC yum -y upgrade +- # Install necessary build/test requirements +- $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" +- $DOCKER_EXEC python3.6 -m ensurepip +- $DOCKER_EXEC python3.6 -m pip install meson +- # Create necessary symlinks +- $DOCKER_EXEC ln --force -s /usr/bin/python3.6 /usr/bin/python3 +- $DOCKER_EXEC ln --force -s /usr/bin/ninja-build /usr/bin/ninja ++ $DOCKER_EXEC dnf makecache ++ # Install and enable EPEL ++ $DOCKER_EXEC dnf -q -y install epel-release dnf-utils "${ADDITIONAL_DEPS[@]}" ++ $DOCKER_EXEC dnf config-manager -q --enable epel ++ # Upgrade the container to get the most recent environment ++ $DOCKER_EXEC dnf -y upgrade ++ # Install systemd's build dependencies ++ $DOCKER_EXEC dnf -q -y --enablerepo "PowerTools" builddep systemd + ;; + RUN) + info "Run phase" +@@ -117,16 +110,9 @@ for phase in "${PHASES[@]}"; do + # unexpected fails due to incompatibilities with older systemd + $DOCKER_EXEC ninja -C build install + docker restart $CONT_NAME +- # "Mask" the udev-test.pl, as it requires newer version of systemd-detect-virt +- # and it's pointless to run it on a VM in a Docker container... +- echo -ne "#!/usr/bin/perl\nexit(0);\n" > "test/udev-test.pl" + $DOCKER_EXEC ninja -C build test + ;; + RUN_ASAN|RUN_CLANG_ASAN) +- # Let's install newer gcc for proper ASan/UBSan support +- $DOCKER_EXEC yum -y install centos-release-scl +- $DOCKER_EXEC yum -y install devtoolset-8 devtoolset-8-libasan-devel libasan5 devtoolset-8-libubsan-devel libubsan1 +- $DOCKER_EXEC bash -c "echo 'source scl_source enable devtoolset-8' >> /root/.bashrc" + # Note to my future frustrated self: docker exec runs the given command + # as sh -c 'command' - which means both .bash_profile and .bashrc will + # be ignored. That's because .bash_profile is sourced for LOGIN shells (i.e. diff --git a/SOURCES/0220-travis-drop-SCL-remains.patch b/SOURCES/0220-travis-drop-SCL-remains.patch new file mode 100644 index 0000000..30d5378 --- /dev/null +++ b/SOURCES/0220-travis-drop-SCL-remains.patch @@ -0,0 +1,50 @@ +From 5b14988845b591f6fa2fc1e032618fe882827f4a Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 14 Oct 2019 16:22:51 +0200 +Subject: [PATCH] travis: drop SCL remains + +The `bash -ic` wrapper existed solely to make SCL work as expected + +Resolves: #1761519 +--- + ci/travis-centos-rhel8.sh | 16 +++------------- + 1 file changed, 3 insertions(+), 13 deletions(-) + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index ade44a0413..da131c726b 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -113,22 +113,12 @@ for phase in "${PHASES[@]}"; do + $DOCKER_EXEC ninja -C build test + ;; + RUN_ASAN|RUN_CLANG_ASAN) +- # Note to my future frustrated self: docker exec runs the given command +- # as sh -c 'command' - which means both .bash_profile and .bashrc will +- # be ignored. That's because .bash_profile is sourced for LOGIN shells (i.e. +- # sh -l), whereas .bashrc is sourced for NON-LOGIN INTERACTIVE shells +- # (i.e. sh -i). +- # As the default docker exec command lacks either of those options, +- # we need to use a wrapper command which runs the wanted command +- # under an explicit bash -i, so the SCL source above works properly. +- docker exec -it $CONT_NAME bash -ic 'gcc --version' +- + if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then + ENV_VARS="-e CC=clang -e CXX=clang++" + MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764 + fi +- docker exec $ENV_VARS -it $CONT_NAME bash -ic "meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS ${CONFIGURE_OPTS[@]}" +- docker exec -it $CONT_NAME bash -ic 'ninja -v -C build' ++ docker exec $ENV_VARS -it $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS ${CONFIGURE_OPTS[@]} ++ docker exec -it $CONT_NAME ninja -v -C build + + # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb. + travis_wait docker exec --interactive=false \ +@@ -136,7 +126,7 @@ for phase in "${PHASES[@]}"; do + -e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \ + -e "TRAVIS=$TRAVIS" \ + -t $CONT_NAME \ +- bash -ic 'meson test --timeout-multiplier=3 -C ./build/ --print-errorlogs' ++ meson test --timeout-multiplier=3 -C ./build/ --print-errorlogs + ;; + CLEANUP) + info "Cleanup phase" diff --git a/SOURCES/0221-syslog-fix-segfault-in-syslog_parse_priority.patch b/SOURCES/0221-syslog-fix-segfault-in-syslog_parse_priority.patch new file mode 100644 index 0000000..97f64e0 --- /dev/null +++ b/SOURCES/0221-syslog-fix-segfault-in-syslog_parse_priority.patch @@ -0,0 +1,110 @@ +From 8bd791fb3a8e85063e297204bdef8004aacd22b1 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 8 Aug 2018 18:27:15 +0900 +Subject: [PATCH] syslog: fix segfault in syslog_parse_priority() + +(cherry picked from commit a5ee33b951cfa22db53d0274c9c6c0d9d4dae39d) + +Resolves: #1761519 +--- + src/basic/syslog-util.c | 20 +++++++++++--------- + src/journal/test-journal-syslog.c | 20 ++++++++++++++++++++ + 2 files changed, 31 insertions(+), 9 deletions(-) + +diff --git a/src/basic/syslog-util.c b/src/basic/syslog-util.c +index 21461fa581..fe129482f3 100644 +--- a/src/basic/syslog-util.c ++++ b/src/basic/syslog-util.c +@@ -10,7 +10,8 @@ + + int syslog_parse_priority(const char **p, int *priority, bool with_facility) { + int a = 0, b = 0, c = 0; +- int k; ++ const char *end; ++ size_t k; + + assert(p); + assert(*p); +@@ -19,21 +20,22 @@ int syslog_parse_priority(const char **p, int *priority, bool with_facility) { + if ((*p)[0] != '<') + return 0; + +- if (!strchr(*p, '>')) ++ end = strchr(*p, '>'); ++ if (!end) + return 0; + +- if ((*p)[2] == '>') { ++ k = end - *p; ++ assert(k > 0); ++ ++ if (k == 2) + c = undecchar((*p)[1]); +- k = 3; +- } else if ((*p)[3] == '>') { ++ else if (k == 3) { + b = undecchar((*p)[1]); + c = undecchar((*p)[2]); +- k = 4; +- } else if ((*p)[4] == '>') { ++ } else if (k == 4) { + a = undecchar((*p)[1]); + b = undecchar((*p)[2]); + c = undecchar((*p)[3]); +- k = 5; + } else + return 0; + +@@ -46,7 +48,7 @@ int syslog_parse_priority(const char **p, int *priority, bool with_facility) { + else + *priority = (*priority & LOG_FACMASK) | c; + +- *p += k; ++ *p += k + 1; + return 1; + } + +diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c +index 7294cde032..120477cc9f 100644 +--- a/src/journal/test-journal-syslog.c ++++ b/src/journal/test-journal-syslog.c +@@ -4,6 +4,7 @@ + #include "journald-syslog.h" + #include "macro.h" + #include "string-util.h" ++#include "syslog-util.h" + + static void test_syslog_parse_identifier(const char *str, + const char *ident, const char *pid, const char *rest, int ret) { +@@ -19,6 +20,17 @@ static void test_syslog_parse_identifier(const char *str, + assert_se(streq(buf, rest)); + } + ++static void test_syslog_parse_priority(const char *str, int priority, int ret) { ++ const char *buf = str; ++ int priority2, ret2; ++ ++ ret2 = syslog_parse_priority(&buf, &priority2, false); ++ ++ assert_se(ret == ret2); ++ if (ret2 == 1) ++ assert_se(priority == priority2); ++} ++ + int main(void) { + test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", "xxx", 11); + test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, "xxx", 6); +@@ -33,5 +45,13 @@ int main(void) { + test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6); + test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0); + ++ test_syslog_parse_priority("<>", 0, 0); ++ test_syslog_parse_priority("<>aaa", 0, 0); ++ test_syslog_parse_priority("", 0, 0); ++ test_syslog_parse_priority("aaa", 0, 0); ++ test_syslog_parse_priority(" ", 0, 0); ++ test_syslog_parse_priority(" aaa", 0, 0); ++ /* TODO: add test cases of valid priorities */ ++ + return 0; + } diff --git a/SOURCES/0222-sd-bus-make-strict-asan-shut-up.patch b/SOURCES/0222-sd-bus-make-strict-asan-shut-up.patch new file mode 100644 index 0000000..bfe310a --- /dev/null +++ b/SOURCES/0222-sd-bus-make-strict-asan-shut-up.patch @@ -0,0 +1,45 @@ +From fbe5fa22f5b99d4e444db54aadb661e9c932eb6c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 16 Nov 2018 13:00:40 +0100 +Subject: [PATCH] sd-bus: make strict asan shut up + +asan doesn't like it if we use strndup() (i.e. a string function) on a +non-NULL terminated buffer (i.e. something that isn't really a string). + +Let's hence use memdup_suffix0() instead of strndup(), which is more +appropriate for binary data that is to become a string. + +Fixes: #10385 +(cherry picked from commit ac0a94f7438b49a0890d9806db1fa211a5bca10a) + +Resolves: #1761519 +--- + src/libsystemd/sd-bus/bus-message.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 53cbd675b7..19cb2b9a97 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -5101,6 +5101,7 @@ int bus_message_parse_fields(sd_bus_message *m) { + return -EBADMSG; + + if (*p == 0) { ++ char *k; + size_t l; + + /* We found the beginning of the signature +@@ -5114,9 +5115,11 @@ int bus_message_parse_fields(sd_bus_message *m) { + p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END) + return -EBADMSG; + +- if (free_and_strndup(&m->root_container.signature, +- p + 1 + 1, l - 2) < 0) ++ k = memdup_suffix0(p + 1 + 1, l - 2); ++ if (!k) + return -ENOMEM; ++ ++ free_and_replace(m->root_container.signature, k); + break; + } + diff --git a/SOURCES/0223-travis-don-t-run-slow-tests-under-ASan-UBSan.patch b/SOURCES/0223-travis-don-t-run-slow-tests-under-ASan-UBSan.patch new file mode 100644 index 0000000..d5df9f1 --- /dev/null +++ b/SOURCES/0223-travis-don-t-run-slow-tests-under-ASan-UBSan.patch @@ -0,0 +1,43 @@ +From 2f44943836b69455792a5422673f8a69bc9705ba Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 14 Oct 2019 17:14:35 +0200 +Subject: [PATCH] travis: don't run slow tests under ASan/UBSan + +Resolves: #1761519 +--- + ci/travis-centos-rhel8.sh | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index da131c726b..a1502e15ee 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -65,10 +65,6 @@ CONFIGURE_OPTS=( + -Dnetworkd=false + -Dtimesyncd=false + -Ddefault-hierarchy=legacy +- # Custom options +- -Dslow-tests=true +- -Dtests=unsafe +- -Dinstall-tests=true + ) + + function info() { +@@ -104,7 +100,7 @@ for phase in "${PHASES[@]}"; do + RUN) + info "Run phase" + # Build systemd +- docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build "${CONFIGURE_OPTS[@]}" ++ docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}" + $DOCKER_EXEC ninja -v -C build + # Let's install the new systemd and "reboot" the container to avoid + # unexpected fails due to incompatibilities with older systemd +@@ -117,7 +113,7 @@ for phase in "${PHASES[@]}"; do + ENV_VARS="-e CC=clang -e CXX=clang++" + MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764 + fi +- docker exec $ENV_VARS -it $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS ${CONFIGURE_OPTS[@]} ++ docker exec $ENV_VARS -it $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}" + docker exec -it $CONT_NAME ninja -v -C build + + # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb. diff --git a/SOURCES/0224-kernel-install-do-not-require-non-empty-kernel-cmdli.patch b/SOURCES/0224-kernel-install-do-not-require-non-empty-kernel-cmdli.patch new file mode 100644 index 0000000..b79434e --- /dev/null +++ b/SOURCES/0224-kernel-install-do-not-require-non-empty-kernel-cmdli.patch @@ -0,0 +1,67 @@ +From 6240d78097c6f828aa2ca3b50ac322b41dc41fd1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 23 Aug 2019 11:34:45 +0200 +Subject: [PATCH] kernel-install: do not require non-empty kernel cmdline + +When booting with Fedora-Server-dvd-x86_64-30-20190411.n.0.iso, +/proc/cmdline is empty (libvirt, qemu host with bios, not sure if that +matters), after installation to disk, anaconda would "crash" in kernel-core +%posttrans, after calling kernel-install, because dracut would fail +with + +> Could not determine the kernel command line parameters. +> Please specify the kernel command line in /etc/kernel/cmdline! + +I guess it's legitimate, even if unusual, to have no cmdline parameters. +Two changes are done in this patch: + +1. do not fail if the cmdline is empty. +2. if /usr/lib/kernel/cmdline or /etc/kernel/cmdline are present, but + empty, ignore /proc/cmdline. If there's explicit configuration to + have empty cmdline, don't ignore it. + +The same change was done in dracut: +https://github.com/dracutdevs/dracut/pull/561. + +(cherry picked from commit 88e1306af6380794842fb31108ba67895799fab4) + +Resolves: #1701454 +--- + src/kernel-install/90-loaderentry.install | 14 ++++---------- + 1 file changed, 4 insertions(+), 10 deletions(-) + +diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install +index a271cdb8a0..1619301536 100644 +--- a/src/kernel-install/90-loaderentry.install ++++ b/src/kernel-install/90-loaderentry.install +@@ -43,13 +43,13 @@ if ! [[ $PRETTY_NAME ]]; then + PRETTY_NAME="Linux $KERNEL_VERSION" + fi + +-declare -a BOOT_OPTIONS +- + if [[ -f /etc/kernel/cmdline ]]; then + read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline +-fi ++elif [[ -f /usr/lib/kernel/cmdline ]]; then ++ read -r -d '' -a BOOT_OPTIONS < /usr/lib/kernel/cmdline ++else ++ declare -a BOOT_OPTIONS + +-if ! [[ ${BOOT_OPTIONS[*]} ]]; then + read -r -d '' -a line < /proc/cmdline + for i in "${line[@]}"; do + [[ "${i#initrd=*}" != "$i" ]] && continue +@@ -57,12 +57,6 @@ if ! [[ ${BOOT_OPTIONS[*]} ]]; then + done + fi + +-if ! [[ ${BOOT_OPTIONS[*]} ]]; then +- echo "Could not determine the kernel command line parameters." >&2 +- echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2 +- exit 1 +-fi +- + cp "$KERNEL_IMAGE" "$BOOT_DIR_ABS/linux" && + chown root:root "$BOOT_DIR_ABS/linux" && + chmod 0644 "$BOOT_DIR_ABS/linux" || { diff --git a/SOURCES/0225-ask-password-prevent-buffer-overrow-when-reading-fro.patch b/SOURCES/0225-ask-password-prevent-buffer-overrow-when-reading-fro.patch new file mode 100644 index 0000000..1a3af20 --- /dev/null +++ b/SOURCES/0225-ask-password-prevent-buffer-overrow-when-reading-fro.patch @@ -0,0 +1,36 @@ +From c6c8e0d097d6ba12471c6112c3fd339ea40329d5 Mon Sep 17 00:00:00 2001 +From: Thadeu Lima de Souza Cascardo +Date: Mon, 13 May 2019 16:58:01 -0300 +Subject: [PATCH] ask-password: prevent buffer overrow when reading from + keyring + +When we read from keyring, a temporary buffer is allocated in order to +determine the size needed for the entire data. However, when zeroing that area, +we use the data size returned by the read instead of the lesser size allocate +for the buffer. + +That will cause memory corruption that causes systemd-cryptsetup to crash +either when a single large password is used or when multiple passwords have +already been pushed to the keyring. + +Signed-off-by: Thadeu Lima de Souza Cascardo +(cherry picked from commit 59c55e73eaee345e1ee67c23eace8895ed499693) + +Resolves: #1752050 +--- + src/shared/ask-password-api.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c +index 682dc754fc..764ebd08e1 100644 +--- a/src/shared/ask-password-api.c ++++ b/src/shared/ask-password-api.c +@@ -79,7 +79,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) { + if (n < m) + break; + +- explicit_bzero(p, n); ++ explicit_bzero(p, m); + free(p); + m *= 2; + } diff --git a/SOURCES/0226-core-try-to-reopen-dev-kmsg-again-right-after-mounti.patch b/SOURCES/0226-core-try-to-reopen-dev-kmsg-again-right-after-mounti.patch new file mode 100644 index 0000000..d568e76 --- /dev/null +++ b/SOURCES/0226-core-try-to-reopen-dev-kmsg-again-right-after-mounti.patch @@ -0,0 +1,37 @@ +From 985837dab9c892858a92ae50043843307f5e0714 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 19 Jul 2019 18:29:11 +0200 +Subject: [PATCH] core: try to reopen /dev/kmsg again right after mounting /dev + +I was debugging stuff during early boot, and was confused that I never +found the logs for it in kmsg. The reason for that was that /proc is +generally not mounted the first time we do log_open() and hence +log_set_target(LOG_TARGET_KMSG) we do when running as PID 1 had not +effect. A lot later during start-up we call log_open() again where this +is fixed (after the point where we close all remaining fds still open), +but in the meantime no logs every got written to kmsg. This patch fixes +that. + +(cherry picked from commit 0a2eef1ee1fef74be9d12f7dc4d0006b645b579c) + +Resolves: #1749212 +--- + src/core/main.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/src/core/main.c b/src/core/main.c +index 44dd8348be..af7b26d6f1 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -2215,6 +2215,11 @@ int main(int argc, char *argv[]) { + goto finish; + } + ++ /* Let's open the log backend a second time, in case the first time didn't ++ * work. Quite possibly we have mounted /dev just now, so /dev/kmsg became ++ * available, and it previously wasn't. */ ++ log_open(); ++ + r = initialize_security( + &loaded_policy, + &security_start_timestamp, diff --git a/SOURCES/0227-buildsys-don-t-garbage-collect-sections-while-linkin.patch b/SOURCES/0227-buildsys-don-t-garbage-collect-sections-while-linkin.patch new file mode 100644 index 0000000..d305484 --- /dev/null +++ b/SOURCES/0227-buildsys-don-t-garbage-collect-sections-while-linkin.patch @@ -0,0 +1,29 @@ +From 9f259b46b760b2aa08ac1fe76fe61df514e2768f Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 3 Sep 2019 10:05:42 +0200 +Subject: [PATCH] buildsys: don't garbage collect sections while linking + +gc-sections is actually very aggressive and garbage collects ELF +sections used by annobin gcc plugin and annocheck then reports gaps in +coverage. Let's drop that linker flag. + +RHEL-only + +Resolves: #1748258 +--- + meson.build | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/meson.build b/meson.build +index 04b461dcd4..613a5133b6 100644 +--- a/meson.build ++++ b/meson.build +@@ -357,8 +357,6 @@ if get_option('buildtype') != 'debug' + '-ffunction-sections', + '-fdata-sections', + ] +- +- possible_link_flags += '-Wl,--gc-sections' + endif + + add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'c') diff --git a/SOURCES/0228-udev-introduce-CONST-key-name.patch b/SOURCES/0228-udev-introduce-CONST-key-name.patch new file mode 100644 index 0000000..127eea3 --- /dev/null +++ b/SOURCES/0228-udev-introduce-CONST-key-name.patch @@ -0,0 +1,186 @@ +From d6210c3d053d70175d72ed1d1719497eed76000b Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Thu, 17 Oct 2019 09:37:35 +0200 +Subject: [PATCH] udev: introduce CONST key name + +Currently, there is no way to match against system-wide constants, such +as architecture or virtualization type, without forking helper binaries. +That potentially results in a huge number of spawned processes which +output always the same answer. + +This patch introduces a special CONST keyword which takes a hard-coded +string as its key and returns a value assigned to that key. Currently +implemented are CONST{arch} and CONST{virt}, which can be used to match +against the system's architecture and virtualization type. + +(based on commit 4801d8afe2ff1c1c075c9f0bc5631612172e0bb7) + +Resolves: #1762679 +--- + man/udev.xml | 26 ++++++++++++++++++++++++++ + rules/40-redhat.rules | 6 +++--- + src/udev/udev-rules.c | 32 ++++++++++++++++++++++++++++++++ + test/rule-syntax-check.py | 2 +- + 4 files changed, 62 insertions(+), 4 deletions(-) + +diff --git a/man/udev.xml b/man/udev.xml +index bdf901a8f0..8c1eb41787 100644 +--- a/man/udev.xml ++++ b/man/udev.xml +@@ -236,6 +236,32 @@ + + + ++ ++ CONST{key} ++ ++ Match against a system-wide constant. Supported keys are: ++ ++ ++ arch ++ ++ System's architecture. See in ++ systemd.unit5 ++ for possible values. ++ ++ ++ ++ virt ++ ++ System's virtualization environment. See ++ systemd-detect-virt1 ++ for possible values. ++ ++ ++ ++ Unknown keys will never match. ++ ++ ++ + + TAG + +diff --git a/rules/40-redhat.rules b/rules/40-redhat.rules +index fadc6e59f1..3c95cd2df0 100644 +--- a/rules/40-redhat.rules ++++ b/rules/40-redhat.rules +@@ -6,11 +6,11 @@ SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}=="0", ATTR{online} + # Memory hotadd request + SUBSYSTEM!="memory", GOTO="memory_hotplug_end" + ACTION!="add", GOTO="memory_hotplug_end" +-PROGRAM="/bin/uname -p", RESULT=="s390*", GOTO="memory_hotplug_end" +-PROGRAM="/bin/uname -p", RESULT=="ppc64*", GOTO="memory_hotplug_end" ++CONST{arch}=="s390*", GOTO="memory_hotplug_end" ++CONST{arch}=="ppc64*", GOTO="memory_hotplug_end" + + ENV{.state}="online" +-PROGRAM="/bin/systemd-detect-virt", RESULT=="none", ENV{.state}="online_movable" ++CONST{virt}=="none", ENV{.state}="online_movable" + ATTR{state}=="offline", ATTR{state}="$env{.state}" + + LABEL="memory_hotplug_end" +diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c +index 58af863f3d..a246cbe67e 100644 +--- a/src/udev/udev-rules.c ++++ b/src/udev/udev-rules.c +@@ -17,6 +17,7 @@ + #include + + #include "alloc-util.h" ++#include "architecture.h" + #include "conf-files.h" + #include "dirent-util.h" + #include "escape.h" +@@ -34,6 +35,7 @@ + #include "udev.h" + #include "user-util.h" + #include "util.h" ++#include "virt.h" + + #define PREALLOC_TOKEN 2048 + +@@ -123,6 +125,7 @@ enum token_type { + TK_M_DEVLINK, /* val */ + TK_M_NAME, /* val */ + TK_M_ENV, /* val, attr */ ++ TK_M_CONST, /* val, attr */ + TK_M_TAG, /* val */ + TK_M_SUBSYSTEM, /* val */ + TK_M_DRIVER, /* val */ +@@ -259,6 +262,7 @@ static const char *token_str(enum token_type type) { + [TK_M_DEVLINK] = "M DEVLINK", + [TK_M_NAME] = "M NAME", + [TK_M_ENV] = "M ENV", ++ [TK_M_CONST] = "M CONST", + [TK_M_TAG] = "M TAG", + [TK_M_SUBSYSTEM] = "M SUBSYSTEM", + [TK_M_DRIVER] = "M DRIVER", +@@ -370,6 +374,7 @@ static void dump_token(struct udev_rules *rules, struct token *token) { + case TK_M_SYSCTL: + case TK_M_ATTRS: + case TK_M_ENV: ++ case TK_M_CONST: + case TK_A_ATTR: + case TK_A_SYSCTL: + case TK_A_ENV: +@@ -903,6 +908,7 @@ static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, + token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; + break; + case TK_M_ENV: ++ case TK_M_CONST: + case TK_M_ATTR: + case TK_M_SYSCTL: + case TK_M_ATTRS: +@@ -1226,6 +1232,17 @@ static void add_rule(struct udev_rules *rules, char *line, + rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr); + } + ++ } else if (startswith(key, "CONST{")) { ++ attr = get_key_attribute(rules->udev, key + STRLEN("CONST")); ++ if (attr == NULL || !STR_IN_SET(attr, "arch", "virt")) ++ LOG_AND_RETURN("error parsing %s attribute", "CONST"); ++ ++ if (op == OP_REMOVE) ++ LOG_AND_RETURN("invalid %s operation", "CONST"); ++ ++ if (op < OP_MATCH_MAX) ++ rule_add_key(&rule_tmp, TK_M_CONST, op, value, attr); ++ + } else if (streq(key, "TAG")) { + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); +@@ -1855,6 +1872,21 @@ void udev_rules_apply_to_event(struct udev_rules *rules, + goto nomatch; + break; + } ++ case TK_M_CONST: { ++ const char *key_name = rules_str(rules, cur->key.attr_off); ++ const char *value = NULL; ++ ++ if (streq(key_name, "arch")) { ++ value = architecture_to_string(uname_architecture()); ++ } else if (streq(key_name, "virt")) { ++ value = virtualization_to_string(detect_virtualization()); ++ } else ++ assert_not_reached("Invalid CONST key"); ++ ++ if (match_key(rules, cur, value)) ++ goto nomatch; ++ break; ++ } + case TK_M_TAG: { + struct udev_list_entry *list_entry; + bool match = false; +diff --git a/test/rule-syntax-check.py b/test/rule-syntax-check.py +index c7c0a1a656..6e59f421f5 100755 +--- a/test/rule-syntax-check.py ++++ b/test/rule-syntax-check.py +@@ -19,7 +19,7 @@ quoted_string_re = r'"(?:[^\\"]|\\.)*"' + no_args_tests = re.compile(r'(ACTION|DEVPATH|KERNELS?|NAME|SYMLINK|SUBSYSTEMS?|DRIVERS?|TAG|PROGRAM|RESULT|TEST)\s*(?:=|!)=\s*' + quoted_string_re + '$') + # PROGRAM can also be specified as an assignment. + program_assign = re.compile(r'PROGRAM\s*=\s*' + quoted_string_re + '$') +-args_tests = re.compile(r'(ATTRS?|ENV|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*' + quoted_string_re + '$') ++args_tests = re.compile(r'(ATTRS?|ENV|CONST|TEST){([a-zA-Z0-9/_.*%-]+)}\s*(?:=|!)=\s*' + quoted_string_re + '$') + no_args_assign = re.compile(r'(NAME|SYMLINK|OWNER|GROUP|MODE|TAG|RUN|LABEL|GOTO|WAIT_FOR|OPTIONS|IMPORT)\s*(?:\+=|:=|=)\s*' + quoted_string_re + '$') + args_assign = re.compile(r'(ATTR|ENV|IMPORT|RUN){([a-zA-Z0-9/_.*%-]+)}\s*(=|\+=)\s*' + quoted_string_re + '$') + # Find comma-separated groups, but allow commas that are inside quoted strings. diff --git a/SOURCES/0229-Call-getgroups-to-know-size-of-supplementary-groups-.patch b/SOURCES/0229-Call-getgroups-to-know-size-of-supplementary-groups-.patch new file mode 100644 index 0000000..655f32a --- /dev/null +++ b/SOURCES/0229-Call-getgroups-to-know-size-of-supplementary-groups-.patch @@ -0,0 +1,50 @@ +From e1bd03e75860fb349a6de589bbb1274acc454aef Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Renaud=20M=C3=A9trich?= +Date: Fri, 13 Sep 2019 11:18:18 +0200 +Subject: [PATCH] Call getgroups() to know size of supplementary groups array + to allocate + +Resolves RHBZ #1743230 - journalctl dumps core when stack limit is reduced to 256 KB + +(cherry picked from commit f5e0b942af1e86993c21f4e5c84342bb10403dac) + +Resolves: #1743235 +--- + src/basic/user-util.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index a562a397c7..c533f67025 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -358,9 +358,8 @@ char* gid_to_name(gid_t gid) { + } + + int in_gid(gid_t gid) { +- long ngroups_max; + gid_t *gids; +- int r, i; ++ int ngroups, r, i; + + if (getgid() == gid) + return 1; +@@ -371,12 +370,15 @@ int in_gid(gid_t gid) { + if (!gid_is_valid(gid)) + return -EINVAL; + +- ngroups_max = sysconf(_SC_NGROUPS_MAX); +- assert(ngroups_max > 0); ++ ngroups = getgroups(0, NULL); ++ if (ngroups < 0) ++ return -errno; ++ if (ngroups == 0) ++ return 0; + +- gids = newa(gid_t, ngroups_max); ++ gids = newa(gid_t, ngroups); + +- r = getgroups(ngroups_max, gids); ++ r = getgroups(ngroups, gids); + if (r < 0) + return -errno; + diff --git a/SOURCES/0230-Consider-smb3-as-remote-filesystem.patch b/SOURCES/0230-Consider-smb3-as-remote-filesystem.patch new file mode 100644 index 0000000..691f180 --- /dev/null +++ b/SOURCES/0230-Consider-smb3-as-remote-filesystem.patch @@ -0,0 +1,30 @@ +From 1bf923686a6842f222b1ef5f5174511340c75685 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 1 Oct 2019 08:45:08 +0200 +Subject: [PATCH] Consider smb3 as remote filesystem + +Currently systemd will treat smb3 as local filesystem and cause +can't boot failures. Add smb3 to the list of remote filesystems +to fix this issue. + +Signed-off-by: Kenneth D'souza + +(cherry picked from commit ff7d6a740b0c6fa3be63d3908a0858730a0837c5) + +Resolves: #1757257 +--- + src/basic/mount-util.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c +index 3670b7f591..5b04e21f34 100644 +--- a/src/basic/mount-util.c ++++ b/src/basic/mount-util.c +@@ -603,6 +603,7 @@ bool fstype_is_network(const char *fstype) { + return STR_IN_SET(fstype, + "afs", + "cifs", ++ "smb3", + "smbfs", + "sshfs", + "ncpfs", diff --git a/SOURCES/0231-process-util-introduce-pid_is_my_child-helper.patch b/SOURCES/0231-process-util-introduce-pid_is_my_child-helper.patch new file mode 100644 index 0000000..5d740cf --- /dev/null +++ b/SOURCES/0231-process-util-introduce-pid_is_my_child-helper.patch @@ -0,0 +1,114 @@ +From f057aa6bb604845fa10ad569bca306e5e1e8fe0d Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Mon, 18 Mar 2019 11:48:34 +0100 +Subject: [PATCH] process-util: introduce pid_is_my_child() helper +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +No functional changes. + +Thanks Renaud Métrich for backporting this to RHEL. +Resolves: #1744972 +--- + src/basic/process-util.c | 14 ++++++++++++++ + src/basic/process-util.h | 1 + + src/core/cgroup.c | 7 ++----- + src/core/service.c | 8 ++------ + 4 files changed, 19 insertions(+), 11 deletions(-) + +diff --git a/src/basic/process-util.c b/src/basic/process-util.c +index aa3eff779a..6dbeee9dda 100644 +--- a/src/basic/process-util.c ++++ b/src/basic/process-util.c +@@ -903,6 +903,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 */ + +diff --git a/src/basic/process-util.h b/src/basic/process-util.h +index a5bb072b25..a3bd2851b4 100644 +--- a/src/basic/process-util.h ++++ b/src/basic/process-util.h +@@ -68,6 +68,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); +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 62ab41a288..b7ed07e65b 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1876,7 +1876,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); +@@ -1889,15 +1889,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) +diff --git a/src/core/service.c b/src/core/service.c +index 24f167572a..614ba05d89 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -139,8 +139,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) +@@ -159,12 +157,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; + } diff --git a/SOURCES/0232-core-reduce-the-number-of-stalled-PIDs-from-the-watc.patch b/SOURCES/0232-core-reduce-the-number-of-stalled-PIDs-from-the-watc.patch new file mode 100644 index 0000000..46cf59e --- /dev/null +++ b/SOURCES/0232-core-reduce-the-number-of-stalled-PIDs-from-the-watc.patch @@ -0,0 +1,323 @@ +From 79e9566ec0a61d887ab63f17192dbd71aae36ee0 Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Mon, 18 Mar 2019 20:59:36 +0100 +Subject: [PATCH] core: reduce the number of stalled PIDs from the watched + processes list when possible +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some PIDs can remain in the watched list even though their processes have +exited since a long time. It can easily happen if the main process of a forking +service manages to spawn a child before the control process exits for example. + +However when a pid is about to be mapped to a unit by calling unit_watch_pid(), +the caller usually knows if the pid should belong to this unit exclusively: if +we just forked() off a child, then we can be sure that its PID is otherwise +unused. In this case we take this opportunity to remove any stalled PIDs from +the watched process list. + +If we learnt about a PID in any other form (for example via PID file, via +searching, MAINPID= and so on), then we can't assume anything. + +Thanks Renaud Métrich for backporting this to RHEL. +Resolves: #1744972 +--- + src/core/cgroup.c | 2 +- + src/core/dbus-scope.c | 2 +- + src/core/manager.c | 10 ++++++++++ + src/core/manager.h | 2 ++ + src/core/mount.c | 5 ++--- + src/core/service.c | 16 ++++++++-------- + src/core/socket.c | 7 +++---- + src/core/swap.c | 5 ++--- + src/core/unit.c | 8 +++++++- + src/core/unit.h | 2 +- + src/test/test-watch-pid.c | 12 ++++++------ + 11 files changed, 43 insertions(+), 28 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index b7ed07e65b..76eafdc082 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1926,7 +1926,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/dbus-scope.c b/src/core/dbus-scope.c +index 6725f62794..0bbf64fff1 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/manager.c b/src/core/manager.c +index c83e296cf3..0eae7d46fb 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -2044,6 +2044,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; +diff --git a/src/core/manager.h b/src/core/manager.h +index c7f4d66ecd..fa47952d24 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -406,6 +406,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_environment_add(Manager *m, char **minus, char **plus); +diff --git a/src/core/mount.c b/src/core/mount.c +index 2ac04e3874..5878814b1b 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -677,7 +677,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; + +@@ -781,9 +781,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; +diff --git a/src/core/service.c b/src/core/service.c +index 614ba05d89..310838a5f6 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -974,7 +974,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); + +@@ -1004,7 +1004,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); +@@ -1135,7 +1135,7 @@ static int service_coldplug(Unit *u) { + SERVICE_RUNNING, SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, 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; + } +@@ -1147,7 +1147,7 @@ static int service_coldplug(Unit *u) { + SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, 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; + } +@@ -1545,8 +1545,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; +@@ -3643,7 +3643,7 @@ static void service_notify_message( + } + if (r > 0) { + service_set_main_pid(s, new_main_pid); +- unit_watch_pid(UNIT(s), new_main_pid); ++ unit_watch_pid(UNIT(s), new_main_pid, false); + notify_dbus = true; + } + } +@@ -3858,7 +3858,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/socket.c b/src/core/socket.c +index d488c64e91..b034549634 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -1816,7 +1816,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; + +@@ -1902,9 +1902,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; +@@ -1973,7 +1972,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; + +diff --git a/src/core/swap.c b/src/core/swap.c +index b644753a1c..e717dbb54a 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -531,7 +531,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; + +@@ -636,9 +636,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; +diff --git a/src/core/unit.c b/src/core/unit.c +index d298afb0d4..b0b1c77ef7 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -2500,7 +2500,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag + 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); +@@ -2508,6 +2508,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; +diff --git a/src/core/unit.h b/src/core/unit.h +index e1a60da244..68cc1869e4 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -655,7 +655,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); + +diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c +index cb43b35bc5..8c70175aed 100644 +--- a/src/test/test-watch-pid.c ++++ b/src/test/test-watch-pid.c +@@ -49,25 +49,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/SOURCES/0233-core-only-watch-processes-when-it-s-really-necessary.patch b/SOURCES/0233-core-only-watch-processes-when-it-s-really-necessary.patch new file mode 100644 index 0000000..058ff69 --- /dev/null +++ b/SOURCES/0233-core-only-watch-processes-when-it-s-really-necessary.patch @@ -0,0 +1,55 @@ +From 25b93538eba0275d35ef4b0792c2cd63d63d5e8d Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Tue, 19 Mar 2019 10:59:26 +0100 +Subject: [PATCH] core: only watch processes when it's really necessary +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If we know that main pid is our child then it's unnecessary to watch all +other processes of a unit since in this case we will get SIGCHLD when the main +process will exit and will act upon accordingly. + +So let's watch all processes only if the main process is not our child since in +this case we need to detect when the cgroup will become empty in order to +figure out when the service becomes dead. This is only needed by cgroupv1. + +Thanks Renaud Métrich for backporting this to RHEL. +Resolves: #1744972 +--- + src/core/service.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 310838a5f6..b1ec52d220 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -3410,8 +3410,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; + +@@ -3433,10 +3432,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) { diff --git a/SOURCES/0234-core-implement-per-unit-journal-rate-limiting.patch b/SOURCES/0234-core-implement-per-unit-journal-rate-limiting.patch new file mode 100644 index 0000000..a503b82 --- /dev/null +++ b/SOURCES/0234-core-implement-per-unit-journal-rate-limiting.patch @@ -0,0 +1,641 @@ +From a26f2b2732733aa361fec0a3a8f0ba377f48e75c Mon Sep 17 00:00:00 2001 +From: Anita Zhang +Date: Sun, 7 Oct 2018 20:28:36 -0700 +Subject: [PATCH] core: implement per unit journal rate limiting + +Add LogRateLimitIntervalSec= and LogRateLimitBurst= options for +services. If provided, these values get passed to the journald +client context, and those values are used in the rate limiting +function in the journal over the the journald.conf values. + +Part of #10230 + +(cherry picked from commit 90fc172e191f44979005a524521112f2bd1ff21b) + +Resolves: #1719577 +--- + catalog/systemd.catalog.in | 3 +- + doc/TRANSIENT-SETTINGS.md | 2 + + man/journald.conf.xml | 8 +- + man/systemd.exec.xml | 16 ++++ + src/core/dbus-execute.c | 8 ++ + src/core/execute.c | 14 ++++ + src/core/execute.h | 3 + + src/core/load-fragment-gperf.gperf.m4 | 2 + + src/core/unit.c | 92 +++++++++++++++++++++ + src/core/unit.h | 2 + + src/journal/journald-context.c | 50 ++++++++++- + src/journal/journald-context.h | 3 + + src/journal/journald-rate-limit.c | 38 ++++----- + src/journal/journald-rate-limit.h | 4 +- + src/journal/journald-server.c | 4 +- + src/shared/bus-unit-util.c | 8 ++ + test/fuzz/fuzz-unit-file/directives.service | 2 + + 17 files changed, 231 insertions(+), 28 deletions(-) + +diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in +index f1bddc6f7d..8234e387cf 100644 +--- a/catalog/systemd.catalog.in ++++ b/catalog/systemd.catalog.in +@@ -52,7 +52,8 @@ dropped, other services' messages are unaffected. + + The limits controlling when messages are dropped may be configured + with RateLimitIntervalSec= and RateLimitBurst= in +-/etc/systemd/journald.conf. See journald.conf(5) for details. ++/etc/systemd/journald.conf or LogRateLimitIntervalSec= and LogRateLimitBurst= ++in the unit file. See journald.conf(5) and systemd.exec(5) for details. + + -- e9bf28e6e834481bb6f48f548ad13606 + Subject: Journal messages have been missed +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index ca9e8387b7..0ea444b133 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -135,6 +135,8 @@ All execution-related settings are available for transient units. + ✓ SyslogLevelPrefix= + ✓ LogLevelMax= + ✓ LogExtraFields= ++✓ LogRateLimitIntervalSec= ++✓ LogRateLimitBurst= + ✓ SecureBits= + ✓ CapabilityBoundingSet= + ✓ AmbientCapabilities= +diff --git a/man/journald.conf.xml b/man/journald.conf.xml +index ee8e8b7faf..b57a244b22 100644 +--- a/man/journald.conf.xml ++++ b/man/journald.conf.xml +@@ -140,7 +140,13 @@ + following units: s, min, + h, ms, + us. To turn off any kind of rate limiting, +- set either value to 0. ++ set either value to 0. ++ ++ If a service provides rate limits for itself through ++ LogRateLimitIntervalSec= and/or LogRateLimitBurst= ++ in systemd.exec5, ++ those values will override the settings specified here. ++ + + + +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index 3bd790b485..737c52bcc4 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -1905,6 +1905,22 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + matching. Assign an empty string to reset the list. + + ++ ++ LogRateLimitIntervalSec= ++ LogRateLimitBurst= ++ ++ Configures the rate limiting that is applied to messages generated by this unit. If, in the ++ time interval defined by LogRateLimitIntervalSec=, more messages than specified in ++ LogRateLimitBurst= are logged by a service, all further messages within the interval are ++ dropped until the interval is over. A message about the number of dropped messages is generated. The time ++ specification for LogRateLimitIntervalSec= may be specified in the following units: "s", ++ "min", "h", "ms", "us" (see ++ systemd.time7 for details). ++ The default settings are set by RateLimitIntervalSec= and RateLimitBurst= ++ configured in journald.conf5. ++ ++ ++ + + SyslogIdentifier= + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index c44970c10c..33a91c012e 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -718,6 +718,8 @@ const sd_bus_vtable bus_exec_vtable[] = { + SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_rate_limit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_rate_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -1073,6 +1075,12 @@ int bus_exec_context_set_transient_property( + if (streq(name, "CPUSchedulingPriority")) + return bus_set_transient_sched_priority(u, name, &c->cpu_sched_priority, message, flags, error); + ++ if (streq(name, "LogRateLimitIntervalUSec")) ++ return bus_set_transient_usec(u, name, &c->log_rate_limit_interval_usec, message, flags, error); ++ ++ if (streq(name, "LogRateLimitBurst")) ++ return bus_set_transient_unsigned(u, name, &c->log_rate_limit_burst, message, flags, error); ++ + if (streq(name, "Personality")) + return bus_set_transient_personality(u, name, &c->personality, message, flags, error); + +diff --git a/src/core/execute.c b/src/core/execute.c +index c62f3cf849..8293c522bc 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -3693,6 +3693,9 @@ void exec_context_done(ExecContext *c) { + + exec_context_free_log_extra_fields(c); + ++ c->log_rate_limit_interval_usec = 0; ++ c->log_rate_limit_burst = 0; ++ + c->stdin_data = mfree(c->stdin_data); + c->stdin_data_size = 0; + } +@@ -4153,6 +4156,17 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { + fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t)); + } + ++ if (c->log_rate_limit_interval_usec > 0) { ++ char buf_timespan[FORMAT_TIMESPAN_MAX]; ++ ++ fprintf(f, ++ "%sLogRateLimitIntervalSec: %s\n", ++ prefix, format_timespan(buf_timespan, sizeof(buf_timespan), c->log_rate_limit_interval_usec, USEC_PER_SEC)); ++ } ++ ++ if (c->log_rate_limit_burst > 0) ++ fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_rate_limit_burst); ++ + if (c->n_log_extra_fields > 0) { + size_t j; + +diff --git a/src/core/execute.h b/src/core/execute.h +index bff1634b88..8c91636adc 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -216,6 +216,9 @@ struct ExecContext { + struct iovec* log_extra_fields; + size_t n_log_extra_fields; + ++ usec_t log_rate_limit_interval_usec; ++ unsigned log_rate_limit_burst; ++ + bool cpu_sched_reset_on_fork; + bool non_blocking; + bool private_tmp; +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 15fb47838c..1066bcfb8f 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -57,6 +57,8 @@ $1.SyslogFacility, config_parse_log_facility, 0, + $1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority) + $1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) + $1.LogLevelMax, config_parse_log_level, 0, offsetof($1, exec_context.log_level_max) ++$1.LogRateLimitIntervalSec, config_parse_sec, 0, offsetof($1, exec_context.log_rate_limit_interval_usec) ++$1.LogRateLimitBurst, config_parse_unsigned, 0, offsetof($1, exec_context.log_rate_limit_burst) + $1.LogExtraFields, config_parse_log_extra_fields, 0, offsetof($1, exec_context) + $1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context) + $1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context.secure_bits) +diff --git a/src/core/unit.c b/src/core/unit.c +index b0b1c77ef7..115739f4c6 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -3245,6 +3245,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { + unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id)); + unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max)); + unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields)); ++ unit_serialize_item(u, f, "exported-log-rate-limit-interval", yes_no(u->exported_log_rate_limit_interval)); ++ unit_serialize_item(u, f, "exported-log-rate-limit-burst", yes_no(u->exported_log_rate_limit_burst)); + + unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base); + if (u->cpu_usage_last != NSEC_INFINITY) +@@ -3508,6 +3510,26 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + + continue; + ++ } else if (streq(l, "exported-log-rate-limit-interval")) { ++ ++ r = parse_boolean(v); ++ if (r < 0) ++ log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v); ++ else ++ u->exported_log_rate_limit_interval = r; ++ ++ continue; ++ ++ } else if (streq(l, "exported-log-rate-limit-burst")) { ++ ++ r = parse_boolean(v); ++ if (r < 0) ++ log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v); ++ else ++ u->exported_log_rate_limit_burst = r; ++ ++ continue; ++ + } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) { + + r = safe_atou64(v, &u->cpu_usage_base); +@@ -5241,6 +5263,60 @@ fail: + return r; + } + ++static int unit_export_log_rate_limit_interval(Unit *u, const ExecContext *c) { ++ _cleanup_free_ char *buf = NULL; ++ const char *p; ++ int r; ++ ++ assert(u); ++ assert(c); ++ ++ if (u->exported_log_rate_limit_interval) ++ return 0; ++ ++ if (c->log_rate_limit_interval_usec == 0) ++ return 0; ++ ++ p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id); ++ ++ if (asprintf(&buf, "%" PRIu64, c->log_rate_limit_interval_usec) < 0) ++ return log_oom(); ++ ++ r = symlink_atomic(buf, p); ++ if (r < 0) ++ return log_unit_debug_errno(u, r, "Failed to create log rate limit interval symlink %s: %m", p); ++ ++ u->exported_log_rate_limit_interval = true; ++ return 0; ++} ++ ++static int unit_export_log_rate_limit_burst(Unit *u, const ExecContext *c) { ++ _cleanup_free_ char *buf = NULL; ++ const char *p; ++ int r; ++ ++ assert(u); ++ assert(c); ++ ++ if (u->exported_log_rate_limit_burst) ++ return 0; ++ ++ if (c->log_rate_limit_burst == 0) ++ return 0; ++ ++ p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id); ++ ++ if (asprintf(&buf, "%u", c->log_rate_limit_burst) < 0) ++ return log_oom(); ++ ++ r = symlink_atomic(buf, p); ++ if (r < 0) ++ return log_unit_debug_errno(u, r, "Failed to create log rate limit burst symlink %s: %m", p); ++ ++ u->exported_log_rate_limit_burst = true; ++ return 0; ++} ++ + void unit_export_state_files(Unit *u) { + const ExecContext *c; + +@@ -5274,6 +5350,8 @@ void unit_export_state_files(Unit *u) { + if (c) { + (void) unit_export_log_level_max(u, c); + (void) unit_export_log_extra_fields(u, c); ++ (void) unit_export_log_rate_limit_interval(u, c); ++ (void) unit_export_log_rate_limit_burst(u, c); + } + } + +@@ -5310,6 +5388,20 @@ void unit_unlink_state_files(Unit *u) { + + u->exported_log_extra_fields = false; + } ++ ++ if (u->exported_log_rate_limit_interval) { ++ p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id); ++ (void) unlink(p); ++ ++ u->exported_log_rate_limit_interval = false; ++ } ++ ++ if (u->exported_log_rate_limit_burst) { ++ p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id); ++ (void) unlink(p); ++ ++ u->exported_log_rate_limit_burst = false; ++ } + } + + int unit_prepare_exec(Unit *u) { +diff --git a/src/core/unit.h b/src/core/unit.h +index 68cc1869e4..99755823eb 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -349,6 +349,8 @@ typedef struct Unit { + bool exported_invocation_id:1; + bool exported_log_level_max:1; + bool exported_log_extra_fields:1; ++ bool exported_log_rate_limit_interval:1; ++ bool exported_log_rate_limit_burst:1; + + /* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If + * == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */ +diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c +index dba3525ed8..c8e97e16de 100644 +--- a/src/journal/journald-context.c ++++ b/src/journal/journald-context.c +@@ -140,6 +140,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { + c->timestamp = USEC_INFINITY; + c->extra_fields_mtime = NSEC_INFINITY; + c->log_level_max = -1; ++ c->log_rate_limit_interval = s->rate_limit_interval; ++ c->log_rate_limit_burst = s->rate_limit_burst; + + r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c); + if (r < 0) { +@@ -151,7 +153,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { + return 0; + } + +-static void client_context_reset(ClientContext *c) { ++static void client_context_reset(Server *s, ClientContext *c) { ++ assert(s); + assert(c); + + c->timestamp = USEC_INFINITY; +@@ -186,6 +189,9 @@ static void client_context_reset(ClientContext *c) { + c->extra_fields_mtime = NSEC_INFINITY; + + c->log_level_max = -1; ++ ++ c->log_rate_limit_interval = s->rate_limit_interval; ++ c->log_rate_limit_burst = s->rate_limit_burst; + } + + static ClientContext* client_context_free(Server *s, ClientContext *c) { +@@ -199,7 +205,7 @@ static ClientContext* client_context_free(Server *s, ClientContext *c) { + if (c->in_lru) + assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0); + +- client_context_reset(c); ++ client_context_reset(s, c); + + return mfree(c); + } +@@ -464,6 +470,42 @@ static int client_context_read_extra_fields( + return 0; + } + ++static int client_context_read_log_rate_limit_interval(ClientContext *c) { ++ _cleanup_free_ char *value = NULL; ++ const char *p; ++ int r; ++ ++ assert(c); ++ ++ if (!c->unit) ++ return 0; ++ ++ p = strjoina("/run/systemd/units/log-rate-limit-interval:", c->unit); ++ r = readlink_malloc(p, &value); ++ if (r < 0) ++ return r; ++ ++ return safe_atou64(value, &c->log_rate_limit_interval); ++} ++ ++static int client_context_read_log_rate_limit_burst(ClientContext *c) { ++ _cleanup_free_ char *value = NULL; ++ const char *p; ++ int r; ++ ++ assert(c); ++ ++ if (!c->unit) ++ return 0; ++ ++ p = strjoina("/run/systemd/units/log-rate-limit-burst:", c->unit); ++ r = readlink_malloc(p, &value); ++ if (r < 0) ++ return r; ++ ++ return safe_atou(value, &c->log_rate_limit_burst); ++} ++ + static void client_context_really_refresh( + Server *s, + ClientContext *c, +@@ -490,6 +532,8 @@ static void client_context_really_refresh( + (void) client_context_read_invocation_id(s, c); + (void) client_context_read_log_level_max(s, c); + (void) client_context_read_extra_fields(s, c); ++ (void) client_context_read_log_rate_limit_interval(c); ++ (void) client_context_read_log_rate_limit_burst(c); + + c->timestamp = timestamp; + +@@ -520,7 +564,7 @@ void client_context_maybe_refresh( + /* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out + * entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */ + if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) { +- client_context_reset(c); ++ client_context_reset(s, c); + goto refresh; + } + +diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h +index 9df3a38eff..5e19c71f14 100644 +--- a/src/journal/journald-context.h ++++ b/src/journal/journald-context.h +@@ -49,6 +49,9 @@ struct ClientContext { + size_t extra_fields_n_iovec; + void *extra_fields_data; + nsec_t extra_fields_mtime; ++ ++ usec_t log_rate_limit_interval; ++ unsigned log_rate_limit_burst; + }; + + int client_context_get( +diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c +index 6a8a36a736..539efb8669 100644 +--- a/src/journal/journald-rate-limit.c ++++ b/src/journal/journald-rate-limit.c +@@ -39,6 +39,10 @@ struct JournalRateLimitGroup { + JournalRateLimit *parent; + + char *id; ++ ++ /* Interval is stored to keep track of when the group expires */ ++ usec_t interval; ++ + JournalRateLimitPool pools[POOLS_MAX]; + uint64_t hash; + +@@ -47,8 +51,6 @@ struct JournalRateLimitGroup { + }; + + struct JournalRateLimit { +- usec_t interval; +- unsigned burst; + + JournalRateLimitGroup* buckets[BUCKETS_MAX]; + JournalRateLimitGroup *lru, *lru_tail; +@@ -58,18 +60,13 @@ struct JournalRateLimit { + uint8_t hash_key[16]; + }; + +-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) { ++JournalRateLimit *journal_rate_limit_new(void) { + JournalRateLimit *r; + +- assert(interval > 0 || burst == 0); +- + r = new0(JournalRateLimit, 1); + if (!r) + return NULL; + +- r->interval = interval; +- r->burst = burst; +- + random_bytes(r->hash_key, sizeof(r->hash_key)); + + return r; +@@ -109,7 +106,7 @@ _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, us + assert(g); + + for (i = 0; i < POOLS_MAX; i++) +- if (g->pools[i].begin + g->parent->interval >= ts) ++ if (g->pools[i].begin + g->interval >= ts) + return false; + + return true; +@@ -126,7 +123,7 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { + journal_rate_limit_group_free(r->lru_tail); + } + +-static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) { ++static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) { + JournalRateLimitGroup *g; + struct siphash state; + +@@ -145,6 +142,8 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, + string_hash_func(g->id, &state); + g->hash = siphash24_finalize(&state); + ++ g->interval = interval; ++ + journal_rate_limit_vacuum(r, ts); + + LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g); +@@ -189,7 +188,7 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) { + return burst; + } + +-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { ++int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) { + uint64_t h; + JournalRateLimitGroup *g; + JournalRateLimitPool *p; +@@ -209,11 +208,6 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u + if (!r) + return 1; + +- if (r->interval == 0 || r->burst == 0) +- return 1; +- +- burst = burst_modulate(r->burst, available); +- + ts = now(CLOCK_MONOTONIC); + + siphash24_init(&state, r->hash_key); +@@ -226,10 +220,16 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u + break; + + if (!g) { +- g = journal_rate_limit_group_new(r, id, ts); ++ g = journal_rate_limit_group_new(r, id, rl_interval, ts); + if (!g) + return -ENOMEM; +- } ++ } else ++ g->interval = rl_interval; ++ ++ if (rl_interval == 0 || rl_burst == 0) ++ return 1; ++ ++ burst = burst_modulate(rl_burst, available); + + p = &g->pools[priority_map[priority]]; + +@@ -240,7 +240,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u + return 1; + } + +- if (p->begin + r->interval < ts) { ++ if (p->begin + rl_interval < ts) { + unsigned s; + + s = p->suppressed; +diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h +index 3a7f106de0..a2992800fe 100644 +--- a/src/journal/journald-rate-limit.h ++++ b/src/journal/journald-rate-limit.h +@@ -5,6 +5,6 @@ + + typedef struct JournalRateLimit JournalRateLimit; + +-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst); ++JournalRateLimit *journal_rate_limit_new(void); + void journal_rate_limit_free(JournalRateLimit *r); +-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available); ++int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available); +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 8de45552f6..0c983e102a 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -943,7 +943,7 @@ void server_dispatch_message( + if (c && c->unit) { + (void) determine_space(s, &available, NULL); + +- rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available); ++ rl = journal_rate_limit_test(s->rate_limit, c->unit, c->log_rate_limit_interval, c->log_rate_limit_burst, priority & LOG_PRIMASK, available); + if (rl == 0) + return; + +@@ -1852,7 +1852,7 @@ int server_init(Server *s) { + if (!s->udev) + return -ENOMEM; + +- s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); ++ s->rate_limit = journal_rate_limit_new(); + if (!s->rate_limit) + return -ENOMEM; + +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 3238b442c0..271cc054da 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -755,6 +755,14 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + + return bus_append_parse_nsec(m, field, eq); + ++ if (STR_IN_SET(field, "LogRateLimitIntervalSec")) ++ ++ return bus_append_parse_sec_rename(m, field, eq); ++ ++ if (streq(field, "LogRateLimitBurst")) ++ ++ return bus_append_safe_atou(m, field, eq); ++ + if (streq(field, "MountFlags")) + + return bus_append_mount_propagation_flags_from_string(m, field, eq); +diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +index c2334d3b19..d8d1fc68b8 100644 +--- a/test/fuzz/fuzz-unit-file/directives.service ++++ b/test/fuzz/fuzz-unit-file/directives.service +@@ -792,6 +792,8 @@ LineMax= + LockPersonality= + LogExtraFields= + LogLevelMax= ++LogRateLimitIntervalSec= ++LogRateLimitBurst= + LogsDirectory= + LogsDirectoryMode= + MACVLAN= diff --git a/SOURCES/0235-path-stop-watching-path-specs-once-we-triggered-the-.patch b/SOURCES/0235-path-stop-watching-path-specs-once-we-triggered-the-.patch new file mode 100644 index 0000000..c538f50 --- /dev/null +++ b/SOURCES/0235-path-stop-watching-path-specs-once-we-triggered-the-.patch @@ -0,0 +1,35 @@ +From 55d9d6dfb731d2f1c8c940fb8a7ea0af6c498c4c Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 9 Sep 2019 14:38:35 +0200 +Subject: [PATCH] path: stop watching path specs once we triggered the target + unit + +We start watching them again once we get a notification that triggered +unit entered inactive or failed state. + +Fixes: #10503 +(cherry picked from commit 8fca6944c2ee20c63d62154c8badddc77170b176) + +Resolves: #1763161 +--- + src/core/path.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/core/path.c b/src/core/path.c +index 68b13b610a..5ef178a46b 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -478,11 +478,9 @@ static void path_enter_running(Path *p) { + + p->inotify_triggered = false; + +- r = path_watch(p); +- if (r < 0) +- goto fail; +- + path_set_state(p, PATH_RUNNING); ++ path_unwatch(p); ++ + return; + + fail: diff --git a/SOURCES/0236-journald-fixed-assertion-failure-when-system-journal.patch b/SOURCES/0236-journald-fixed-assertion-failure-when-system-journal.patch new file mode 100644 index 0000000..25c91dd --- /dev/null +++ b/SOURCES/0236-journald-fixed-assertion-failure-when-system-journal.patch @@ -0,0 +1,28 @@ +From 33aa231f5bf3335cdacfb38ffba757865019ce4d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Renaud=20M=C3=A9trich?= + <1163635+rmetrich@users.noreply.github.com> +Date: Mon, 3 Sep 2018 05:42:39 +0200 +Subject: [PATCH] journald: fixed assertion failure when system journal + rotation fails (#9893) + +(cherry picked from commit fd790d6f09b10a87b007b71403cb018f18ff91c9) + +Resolves: #1763619 +--- + src/journal/journald-server.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 0c983e102a..6aecb67d6c 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -1041,7 +1041,8 @@ int server_flush_to_var(Server *s, bool require_flag_file) { + r = 0; + + finish: +- journal_file_post_change(s->system_journal); ++ if (s->system_journal) ++ journal_file_post_change(s->system_journal); + + s->runtime_journal = journal_file_close(s->runtime_journal); + diff --git a/SOURCES/0237-test-use-PBKDF2-instead-of-Argon2-in-cryptsetup.patch b/SOURCES/0237-test-use-PBKDF2-instead-of-Argon2-in-cryptsetup.patch new file mode 100644 index 0000000..4599b60 --- /dev/null +++ b/SOURCES/0237-test-use-PBKDF2-instead-of-Argon2-in-cryptsetup.patch @@ -0,0 +1,29 @@ +From a7f18f9ef4abc7e0732d1710ead2a18a38c3ec6d Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Mar 2019 10:05:33 +0100 +Subject: [PATCH] test: use PBKDF2 instead of Argon2 in cryptsetup... + +to reduce memory requirements for volume manipulation. Also, +to further improve the test performance, reduce number of PBKDF +iterations to 1000 (allowed minimum). + +(cherry picked from commit 5b69d297c153478f6f5e74ba66e1f4e5b6422baf) + +Related: #1761519 +--- + test/TEST-02-CRYPTSETUP/test.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh +index 545602e17a..c38e56f72e 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/SOURCES/0238-test-mask-several-unnecessary-services.patch b/SOURCES/0238-test-mask-several-unnecessary-services.patch new file mode 100644 index 0000000..6ff56e8 --- /dev/null +++ b/SOURCES/0238-test-mask-several-unnecessary-services.patch @@ -0,0 +1,252 @@ +From c748b95f5a00b6d9c46026c3d251c40437e6b64a Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 1 Nov 2018 17:26:36 +0900 +Subject: [PATCH] test: mask several unnecessary services + +This may make CIs run faster. + +(cherry picked from commit 056ae88152a722bdbea54ff33db815d585c8b9c6) + +Related: #1761519 +--- + test/TEST-02-CRYPTSETUP/test.sh | 8 ++++++++ + test/TEST-03-JOBS/test.sh | 8 ++++++++ + test/TEST-04-JOURNAL/test.sh | 8 ++++++++ + test/TEST-05-RLIMITS/test.sh | 8 ++++++++ + test/TEST-07-ISSUE-1981/test.sh | 8 ++++++++ + test/TEST-11-ISSUE-3166/test.sh | 8 ++++++++ + test/TEST-12-ISSUE-3171/test.sh | 8 ++++++++ + test/TEST-13-NSPAWN-SMOKE/test.sh | 8 ++++++++ + test/TEST-18-FAILUREACTION/test.sh | 7 +++++++ + test/TEST-19-DELEGATE/test.sh | 8 ++++++++ + test/TEST-20-MAINPIDGAMES/test.sh | 8 ++++++++ + test/TEST-23-TYPE-EXEC/test.sh | 8 ++++++++ + 12 files changed, 95 insertions(+) + +diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh +index c38e56f72e..97eb2f409e 100755 +--- a/test/TEST-02-CRYPTSETUP/test.sh ++++ b/test/TEST-02-CRYPTSETUP/test.sh +@@ -45,6 +45,14 @@ test_setup() { + + setup_basic_environment + ++ # mask some services that we do not want to run in these tests ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service ++ + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system.conf <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service <$initdir/etc/systemd/system/testsuite.service < +Date: Mon, 21 Oct 2019 18:39:39 +0200 +Subject: [PATCH] test: bump the second partition's size to 50M + +The former size (10M) caused systemd-journald to crash with SIGABRT when +used on a LUKS2 partition, as the LUKS2 metadata consume a significant +part of the 10M partition, thus leaving no space for the journal file +itself (relevant for TEST-02-CRYPTSETUP). This change has been present +in upstream for a while anyway. + +Related: #1761519 +rhel-only +--- + test/test-functions | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/test-functions b/test/test-functions +index af9d16140f..fe25a501da 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -433,7 +433,7 @@ create_empty_image() { + [ -b "$LOOPDEV" ] || return 1 + echo "LOOPDEV=$LOOPDEV" >> $STATEFILE + sfdisk "$LOOPDEV" < +Date: Wed, 25 Jul 2018 08:06:57 -0700 +Subject: [PATCH] shared/sleep-config: exclude zram devices from hibernation + candidates + +On a host with sufficiently large zram but with no actual swap, logind will +respond to CanHibernate() with yes. With this patch, it will correctly respond +no, unless there are other swap devices to consider. + +(cherry picked from commit 411ae92b407bd7b4549b205ad754bcd0e3dfd81f) + +Resolves: #1763617 +--- + src/shared/sleep-config.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c +index 9e4ce183d3..a1523e3f21 100644 +--- a/src/shared/sleep-config.c ++++ b/src/shared/sleep-config.c +@@ -21,6 +21,7 @@ + #include "log.h" + #include "macro.h" + #include "parse-util.h" ++#include "path-util.h" + #include "sleep-config.h" + #include "string-util.h" + #include "strv.h" +@@ -201,9 +202,18 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us + continue; + } + +- if (streq(type_field, "partition") && endswith(dev_field, "\\040(deleted)")) { +- log_warning("Ignoring deleted swapfile '%s'.", dev_field); +- continue; ++ if (streq(type_field, "partition")) { ++ if (endswith(dev_field, "\\040(deleted)")) { ++ log_warning("Ignoring deleted swapfile '%s'.", dev_field); ++ continue; ++ } ++ ++ const char *fn; ++ fn = path_startswith(dev_field, "/dev/"); ++ if (fn && startswith(fn, "zram")) { ++ log_debug("Ignoring compressed ram swap device '%s'.", dev_field); ++ continue; ++ } + } + if (device) + *device = TAKE_PTR(dev_field); diff --git a/SOURCES/0241-selinux-don-t-log-SELINUX_INFO-and-SELINUX_WARNING-m.patch b/SOURCES/0241-selinux-don-t-log-SELINUX_INFO-and-SELINUX_WARNING-m.patch new file mode 100644 index 0000000..5845313 --- /dev/null +++ b/SOURCES/0241-selinux-don-t-log-SELINUX_INFO-and-SELINUX_WARNING-m.patch @@ -0,0 +1,45 @@ +From cc3c020a5f4fc577dbd2da769c22b77e37ae4e30 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 26 Feb 2019 17:33:27 +0100 +Subject: [PATCH] selinux: don't log SELINUX_INFO and SELINUX_WARNING messages + to audit + +Previously we logged even info message from libselinux as USER_AVC's to +audit. For example, setting SELinux to permissive mode generated +following audit message, + +time->Tue Feb 26 11:29:29 2019 +type=USER_AVC msg=audit(1551198569.423:334): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='avc: received setenforce notice (enforcing=0) exe="/usr/lib/systemd/systemd" sauid=0 hostname=? addr=? terminal=?' + +This is unnecessary and wrong at the same time. First, kernel already +records audit event that SELinux was switched to permissive mode, also +the type of the message really shouldn't be USER_AVC. + +Let's ignore SELINUX_WARNING and SELINUX_INFO and forward to audit only +USER_AVC's and errors as these two libselinux message types have clear +mapping to audit message types. + +(cherry picked from commit 6227fc14c48c4c17daed4b91f61cdd4aa375790a) + +Resolves: #1763612 +--- + src/core/selinux-access.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c +index 39e994afd7..ada4f8705c 100644 +--- a/src/core/selinux-access.c ++++ b/src/core/selinux-access.c +@@ -112,7 +112,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/SOURCES/0242-sd-device-introduce-log_device_-macros.patch b/SOURCES/0242-sd-device-introduce-log_device_-macros.patch new file mode 100644 index 0000000..6b13e77 --- /dev/null +++ b/SOURCES/0242-sd-device-introduce-log_device_-macros.patch @@ -0,0 +1,47 @@ +From 0160499e86642f159a972be0196bf7c8a1d19ea8 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Oct 2018 12:04:13 +0900 +Subject: [PATCH] sd-device: introduce log_device_*() macros + +(cherry picked from commit b0cba0ca526ed2d86e283a0fcfebdf0a4d4bea9b) + +Related: #1753369 +--- + src/libsystemd/sd-device/device-util.h | 27 ++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h +index 6dcd2645e6..448dfc63d7 100644 +--- a/src/libsystemd/sd-device/device-util.h ++++ b/src/libsystemd/sd-device/device-util.h +@@ -33,3 +33,30 @@ + for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_subsystem_next(enumerator)) ++ ++#define log_device_full(device, level, error, ...) \ ++ ({ \ ++ const char *_sysname = NULL, *_subsystem = NULL; \ ++ sd_device *_d = (device); \ ++ int _level = (level), _error = (error); \ ++ \ ++ if (_d && _unlikely_(log_get_max_level() >= _level)) { \ ++ (void) sd_device_get_sysname(_d, &_sysname); \ ++ (void) sd_device_get_subsystem(_d, &_subsystem); \ ++ } \ ++ log_object_internal(_level, _error, __FILE__, __LINE__, __func__, \ ++ _sysname ? "DEVICE=" : NULL, _sysname, \ ++ _subsystem ? "SUBSYSTEM=" : NULL, _subsystem, ##__VA_ARGS__); \ ++ }) ++ ++#define log_device_debug(link, ...) log_device_full(link, LOG_DEBUG, 0, ##__VA_ARGS__) ++#define log_device_info(link, ...) log_device_full(link, LOG_INFO, 0, ##__VA_ARGS__) ++#define log_device_notice(link, ...) log_device_full(link, LOG_NOTICE, 0, ##__VA_ARGS__) ++#define log_device_warning(link, ...) log_device_full(link, LOG_WARNING, 0, ##__VA_ARGS__) ++#define log_device_error(link, ...) log_device_full(link, LOG_ERR, 0, ##__VA_ARGS__) ++ ++#define log_device_debug_errno(link, error, ...) log_device_full(link, LOG_DEBUG, error, ##__VA_ARGS__) ++#define log_device_info_errno(link, error, ...) log_device_full(link, LOG_INFO, error, ##__VA_ARGS__) ++#define log_device_notice_errno(link, error, ...) log_device_full(link, LOG_NOTICE, error, ##__VA_ARGS__) ++#define log_device_warning_errno(link, error, ...) log_device_full(link, LOG_WARNING, error, ##__VA_ARGS__) ++#define log_device_error_errno(link, error, ...) log_device_full(link, LOG_ERR, error, ##__VA_ARGS__) diff --git a/SOURCES/0243-udev-Add-id-program-and-rule-for-FIDO-security-token.patch b/SOURCES/0243-udev-Add-id-program-and-rule-for-FIDO-security-token.patch new file mode 100644 index 0000000..0d32c08 --- /dev/null +++ b/SOURCES/0243-udev-Add-id-program-and-rule-for-FIDO-security-token.patch @@ -0,0 +1,505 @@ +From 080d3b14470f6ac59f4cfb97a4200ed18df5c260 Mon Sep 17 00:00:00 2001 +From: Fabian Henneke +Date: Wed, 21 Aug 2019 11:17:59 +0200 +Subject: [PATCH] udev: Add id program and rule for FIDO security tokens + +Add a fido_id program meant to be run for devices in the hidraw +subsystem via an IMPORT directive. The program parses the HID report +descriptor and assigns the ID_SECURITY_TOKEN environment variable if a +declared usage matches the FIDO_CTAPHID_USAGE declared in the FIDO CTAP +specification. This replaces the previous approach of whitelisting all +known security token models manually. + +This commit is accompanied by a test suite and a fuzzer target for the +descriptor parsing routine. + +Fixes: #11996. +(cherry picked from commit d45ee2f31a8358db0accde2e7c81777cedadc3c2) + +Resolves: #1753369 +--- + rules/60-fido-id.rules | 7 ++ + rules/meson.build | 1 + + src/fuzz/fuzz-fido-id-desc.c | 23 +++++++ + src/fuzz/fuzz-fido-id-desc.dict | 6 ++ + src/fuzz/meson.build | 4 ++ + src/test/meson.build | 4 ++ + src/test/test-fido-id-desc.c | 85 +++++++++++++++++++++++ + src/udev/fido_id/fido_id.c | 103 ++++++++++++++++++++++++++++ + src/udev/fido_id/fido_id_desc.c | 92 +++++++++++++++++++++++++ + src/udev/fido_id/fido_id_desc.h | 8 +++ + src/udev/meson.build | 3 + + test/fuzz/fuzz-fido-id-desc/crash0 | 1 + + test/fuzz/fuzz-fido-id-desc/crash1 | 1 + + test/fuzz/fuzz-fido-id-desc/report0 | Bin 0 -> 71 bytes + test/fuzz/fuzz-fido-id-desc/report1 | Bin 0 -> 34 bytes + 15 files changed, 338 insertions(+) + create mode 100644 rules/60-fido-id.rules + create mode 100644 src/fuzz/fuzz-fido-id-desc.c + create mode 100644 src/fuzz/fuzz-fido-id-desc.dict + create mode 100644 src/test/test-fido-id-desc.c + create mode 100644 src/udev/fido_id/fido_id.c + create mode 100644 src/udev/fido_id/fido_id_desc.c + create mode 100644 src/udev/fido_id/fido_id_desc.h + create mode 100644 test/fuzz/fuzz-fido-id-desc/crash0 + create mode 100644 test/fuzz/fuzz-fido-id-desc/crash1 + create mode 100644 test/fuzz/fuzz-fido-id-desc/report0 + create mode 100644 test/fuzz/fuzz-fido-id-desc/report1 + +diff --git a/rules/60-fido-id.rules b/rules/60-fido-id.rules +new file mode 100644 +index 0000000000..fcf5079704 +--- /dev/null ++++ b/rules/60-fido-id.rules +@@ -0,0 +1,7 @@ ++# do not edit this file, it will be overwritten on update ++ ++ACTION=="remove", GOTO="fido_id_end" ++ ++SUBSYSTEM=="hidraw", IMPORT{program}="fido_id" ++ ++LABEL="fido_id_end" +diff --git a/rules/meson.build b/rules/meson.build +index b6aae596b6..6363f8bf2e 100644 +--- a/rules/meson.build ++++ b/rules/meson.build +@@ -7,6 +7,7 @@ rules = files(''' + 60-cdrom_id.rules + 60-drm.rules + 60-evdev.rules ++ 60-fido-id.rules + 60-input-id.rules + 60-persistent-alsa.rules + 60-persistent-input.rules +diff --git a/src/fuzz/fuzz-fido-id-desc.c b/src/fuzz/fuzz-fido-id-desc.c +new file mode 100644 +index 0000000000..cf98dee044 +--- /dev/null ++++ b/src/fuzz/fuzz-fido-id-desc.c +@@ -0,0 +1,23 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fido_id/fido_id_desc.h" ++#include "fuzz.h" ++#include "log.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ /* 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); ++ ++ if (size > HID_MAX_DESCRIPTOR_SIZE) ++ return 0; ++ (void) is_fido_security_token_desc(data, size); ++ ++ return 0; ++} +diff --git a/src/fuzz/fuzz-fido-id-desc.dict b/src/fuzz/fuzz-fido-id-desc.dict +new file mode 100644 +index 0000000000..d2d2679e18 +--- /dev/null ++++ b/src/fuzz/fuzz-fido-id-desc.dict +@@ -0,0 +1,6 @@ ++"\xfe" ++"\x00" ++"\x01" ++"\xf1" ++"\xd0" ++"\xf1\xd0\x00\x01" +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 1dbe28e57e..483a952421 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -47,4 +47,8 @@ fuzzers += [ + [libsystemd_journal_remote, + libshared], + []], ++ [['src/fuzz/fuzz-fido-id-desc.c', ++ 'src/udev/fido_id/fido_id_desc.c'], ++ [], ++ []] + ] +diff --git a/src/test/meson.build b/src/test/meson.build +index 0998f59897..4259421f98 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -663,6 +663,10 @@ tests += [ + [['src/test/test-bus-util.c'], + [], + []], ++ [['src/test/test-fido-id-desc.c', ++ 'src/udev/fido_id/fido_id_desc.c'], ++ [], ++ []], + ] + + ############################################################ +diff --git a/src/test/test-fido-id-desc.c b/src/test/test-fido-id-desc.c +new file mode 100644 +index 0000000000..cf55dd3266 +--- /dev/null ++++ b/src/test/test-fido-id-desc.c +@@ -0,0 +1,85 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++#include ++ ++#include "fido_id/fido_id_desc.h" ++#include "macro.h" ++ ++static void test_is_fido_security_token_desc__fido(void) { ++ static const uint8_t FIDO_HID_DESC_1[] = { ++ 0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, ++ 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, ++ 0x40, 0x91, 0x02, 0xc0, ++ }; ++ assert_se(is_fido_security_token_desc(FIDO_HID_DESC_1, sizeof(FIDO_HID_DESC_1)) > 0); ++ ++ static const uint8_t FIDO_HID_DESC_2[] = { ++ 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, ++ 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, ++ 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, ++ 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, 0x29, 0x65, ++ 0x81, 0x00, 0x09, 0x03, 0x75, 0x08, 0x95, 0x08, 0xb1, 0x02, 0xc0, ++ 0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, ++ 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, ++ 0x40, 0x91, 0x02, 0xc0, ++ }; ++ assert_se(is_fido_security_token_desc(FIDO_HID_DESC_2, sizeof(FIDO_HID_DESC_2)) > 0); ++} ++ ++static void test_is_fido_security_token_desc__non_fido(void) { ++ /* Wrong usage page */ ++ static const uint8_t NON_FIDO_HID_DESC_1[] = { ++ 0x06, 0xd0, 0xf0, 0x09, 0x01, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, ++ 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, ++ 0x40, 0x91, 0x02, 0xc0, ++ }; ++ assert_se(is_fido_security_token_desc(NON_FIDO_HID_DESC_1, sizeof(NON_FIDO_HID_DESC_1)) == 0); ++ ++ /* Wrong usage */ ++ static const uint8_t NON_FIDO_HID_DESC_2[] = { ++ 0x06, 0xd0, 0xf1, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, ++ 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, ++ 0x40, 0x91, 0x02, 0xc0, ++ }; ++ assert_se(is_fido_security_token_desc(NON_FIDO_HID_DESC_2, sizeof(NON_FIDO_HID_DESC_2)) == 0); ++ ++ static const uint8_t NON_FIDO_HID_DESC_3[] = { ++ 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, ++ 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, ++ 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, ++ 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, 0x29, 0x65, ++ 0x81, 0x00, 0x09, 0x03, 0x75, 0x08, 0x95, 0x08, 0xb1, 0x02, 0xc0, ++ }; ++ assert_se(is_fido_security_token_desc(NON_FIDO_HID_DESC_3, sizeof(NON_FIDO_HID_DESC_3)) == 0); ++} ++ ++static void test_is_fido_security_token_desc__invalid(void) { ++ /* Size coded on 1 byte, but no byte given */ ++ static const uint8_t INVALID_HID_DESC_1[] = { 0x01 }; ++ assert_se(is_fido_security_token_desc(INVALID_HID_DESC_1, sizeof(INVALID_HID_DESC_1)) < 0); ++ ++ /* Size coded on 2 bytes, but only 1 byte given */ ++ static const uint8_t INVALID_HID_DESC_2[] = { 0x02, 0x01 }; ++ assert_se(is_fido_security_token_desc(INVALID_HID_DESC_2, sizeof(INVALID_HID_DESC_2)) < 0); ++ ++ /* Size coded on 4 bytes, but only 3 bytes given */ ++ static const uint8_t INVALID_HID_DESC_3[] = { 0x03, 0x01, 0x02, 0x03 }; ++ assert_se(is_fido_security_token_desc(INVALID_HID_DESC_3, sizeof(INVALID_HID_DESC_3)) < 0); ++ ++ /* Long item without a size byte */ ++ static const uint8_t INVALID_HID_DESC_4[] = { 0xfe }; ++ assert_se(is_fido_security_token_desc(INVALID_HID_DESC_4, sizeof(INVALID_HID_DESC_4)) < 0); ++ ++ /* Usage pages are coded on at most 2 bytes */ ++ static const uint8_t INVALID_HID_DESC_5[] = { 0x07, 0x01, 0x02, 0x03, 0x04 }; ++ assert_se(is_fido_security_token_desc(INVALID_HID_DESC_5, sizeof(INVALID_HID_DESC_5)) < 0); ++} ++ ++int main(int argc, char *argv[]) { ++ test_is_fido_security_token_desc__fido(); ++ test_is_fido_security_token_desc__non_fido(); ++ test_is_fido_security_token_desc__invalid(); ++ ++ return EXIT_SUCCESS; ++} +diff --git a/src/udev/fido_id/fido_id.c b/src/udev/fido_id/fido_id.c +new file mode 100644 +index 0000000000..7e1cc804f2 +--- /dev/null ++++ b/src/udev/fido_id/fido_id.c +@@ -0,0 +1,103 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++/* ++ * Identifies FIDO CTAP1 ("U2F")/CTAP2 security tokens based on the usage declared in their report ++ * descriptor and outputs suitable environment variables. ++ * ++ * Inspired by Andrew Lutomirski's 'u2f-hidraw-policy.c' ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sd-device.h" ++ ++#include "device-internal.h" ++#include "device-private.h" ++#include "device-util.h" ++#include "fd-util.h" ++#include "fido_id_desc.h" ++#include "log.h" ++#include "macro.h" ++#include "path-util.h" ++#include "string-util.h" ++#include "udev-util.h" ++ ++static int run(int argc, char **argv) { ++ _cleanup_(sd_device_unrefp) struct sd_device *device = NULL; ++ _cleanup_free_ char *desc_path = NULL; ++ _cleanup_close_ int fd = -1; ++ ++ struct sd_device *hid_device; ++ const char *sys_path; ++ uint8_t desc[HID_MAX_DESCRIPTOR_SIZE]; ++ ssize_t desc_len; ++ ++ int r; ++ ++ log_set_target(LOG_TARGET_AUTO); ++ udev_parse_config(); ++ log_parse_environment(); ++ log_open(); ++ ++ if (argc > 2) ++ return log_error_errno(EINVAL, "Usage: %s [SYSFS_PATH]", program_invocation_short_name); ++ ++ if (argc == 1) { ++ r = device_new_from_strv(&device, environ); ++ if (r < 0) ++ return log_error_errno(r, "Failed to get current device from environment: %m"); ++ } else { ++ r = sd_device_new_from_syspath(&device, argv[1]); ++ if (r < 0) ++ return log_error_errno(r, "Failed to get device from syspath: %m"); ++ } ++ ++ r = sd_device_get_parent(device, &hid_device); ++ if (r < 0) ++ return log_device_error_errno(device, r, "Failed to get parent HID device: %m"); ++ ++ r = sd_device_get_syspath(hid_device, &sys_path); ++ if (r < 0) ++ return log_device_error_errno(hid_device, r, "Failed to get syspath for HID device: %m"); ++ ++ desc_path = path_join(NULL, sys_path, "report_descriptor"); ++ if (!desc_path) ++ return log_oom(); ++ ++ fd = open(desc_path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); ++ if (fd < 0) ++ return log_device_error_errno(hid_device, errno, ++ "Failed to open report descriptor at '%s': %m", desc_path); ++ ++ desc_len = read(fd, desc, sizeof(desc)); ++ if (desc_len < 0) ++ return log_device_error_errno(hid_device, errno, ++ "Failed to read report descriptor at '%s': %m", desc_path); ++ if (desc_len == 0) ++ return log_device_debug_errno(hid_device, EINVAL, ++ "Empty report descriptor at '%s'.", desc_path); ++ ++ r = is_fido_security_token_desc(desc, desc_len); ++ if (r < 0) ++ return log_device_debug_errno(hid_device, r, ++ "Failed to parse report descriptor at '%s'.", desc_path); ++ if (r > 0) { ++ printf("ID_FIDO_TOKEN=1\n"); ++ printf("ID_SECURITY_TOKEN=1\n"); ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char *argv[]) { ++ int r; ++ ++ r = run(argc, argv); ++ ++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} +diff --git a/src/udev/fido_id/fido_id_desc.c b/src/udev/fido_id/fido_id_desc.c +new file mode 100644 +index 0000000000..bbfcf93709 +--- /dev/null ++++ b/src/udev/fido_id/fido_id_desc.c +@@ -0,0 +1,92 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++/* Inspired by Andrew Lutomirski's 'u2f-hidraw-policy.c' */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fido_id_desc.h" ++ ++#define HID_RPTDESC_FIRST_BYTE_LONG_ITEM 0xfeu ++#define HID_RPTDESC_TYPE_GLOBAL 0x1u ++#define HID_RPTDESC_TYPE_LOCAL 0x2u ++#define HID_RPTDESC_TAG_USAGE_PAGE 0x0u ++#define HID_RPTDESC_TAG_USAGE 0x0u ++ ++/* ++ * HID usage for FIDO CTAP1 ("U2F") and CTAP2 security tokens. ++ * https://fidoalliance.org/specs/fido-u2f-v1.0-ps-20141009/fido-u2f-u2f_hid.h-v1.0-ps-20141009.txt ++ * https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#usb-discovery ++ * https://www.usb.org/sites/default/files/hutrr48.pdf ++ */ ++#define FIDO_FULL_USAGE_CTAPHID 0xf1d00001u ++ ++/* ++ * Parses a HID report descriptor and identifies FIDO CTAP1 ("U2F")/CTAP2 security tokens based on their ++ * declared usage. ++ * A positive return value indicates that the report descriptor belongs to a FIDO security token. ++ * https://www.usb.org/sites/default/files/documents/hid1_11.pdf (Section 6.2.2) ++ */ ++int is_fido_security_token_desc(const uint8_t *desc, size_t desc_len) { ++ uint32_t usage = 0; ++ ++ for (size_t pos = 0; pos < desc_len; ) { ++ uint8_t tag, type, size_code; ++ size_t size; ++ uint32_t value; ++ ++ /* Report descriptors consists of short items (1-5 bytes) and long items (3-258 bytes). */ ++ if (desc[pos] == HID_RPTDESC_FIRST_BYTE_LONG_ITEM) { ++ /* No long items are defined in the spec; skip them. ++ * The length of the data in a long item is contained in the byte after the long ++ * item tag. The header consists of three bytes: special long item tag, length, ++ * actual tag. */ ++ if (pos + 1 >= desc_len) ++ return -EINVAL; ++ pos += desc[pos + 1] + 3; ++ continue; ++ } ++ ++ /* The first byte of a short item encodes tag, type and size. */ ++ tag = desc[pos] >> 4; /* Bits 7 to 4 */ ++ type = (desc[pos] >> 2) & 0x3; /* Bits 3 and 2 */ ++ size_code = desc[pos] & 0x3; /* Bits 1 and 0 */ ++ /* Size is coded as follows: ++ * 0 -> 0 bytes, 1 -> 1 byte, 2 -> 2 bytes, 3 -> 4 bytes ++ */ ++ size = size_code < 3 ? size_code : 4; ++ /* Consume header byte. */ ++ pos++; ++ ++ /* Extract the item value coded on size bytes. */ ++ if (pos + size > desc_len) ++ return -EINVAL; ++ value = 0; ++ for (size_t i = 0; i < size; i++) ++ value |= (uint32_t) desc[pos + i] << (8 * i); ++ /* Consume value bytes. */ ++ pos += size; ++ ++ if (type == HID_RPTDESC_TYPE_GLOBAL && tag == HID_RPTDESC_TAG_USAGE_PAGE) { ++ /* A usage page is a 16 bit value coded on at most 16 bits. */ ++ if (size > 2) ++ return -EINVAL; ++ /* A usage page sets the upper 16 bits of a following usage. */ ++ usage = (value & 0x0000ffffu) << 16; ++ } ++ ++ if (type == HID_RPTDESC_TYPE_LOCAL && tag == HID_RPTDESC_TAG_USAGE) { ++ /* A usage is a 32 bit value, but is prepended with the current usage page if ++ * coded on less than 4 bytes (that is, at most 2 bytes). */ ++ if (size == 4) ++ usage = value; ++ else ++ usage = (usage & 0xffff0000u) | (value & 0x0000ffffu); ++ if (usage == FIDO_FULL_USAGE_CTAPHID) ++ return 1; ++ } ++ } ++ ++ return 0; ++} +diff --git a/src/udev/fido_id/fido_id_desc.h b/src/udev/fido_id/fido_id_desc.h +new file mode 100644 +index 0000000000..c813a3a454 +--- /dev/null ++++ b/src/udev/fido_id/fido_id_desc.h +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#pragma once ++ ++#include ++#include ++ ++int is_fido_security_token_desc(const uint8_t *desc, size_t desc_len); +diff --git a/src/udev/meson.build b/src/udev/meson.build +index 3bcd2bd3d7..5931a6da7d 100644 +--- a/src/udev/meson.build ++++ b/src/udev/meson.build +@@ -160,6 +160,9 @@ libudev_core = static_library( + foreach prog : [['ata_id/ata_id.c'], + ['cdrom_id/cdrom_id.c'], + ['collect/collect.c'], ++ ['fido_id/fido_id.c', ++ 'fido_id/fido_id_desc.c', ++ 'fido_id/fido_id_desc.h'], + ['scsi_id/scsi_id.c', + 'scsi_id/scsi_id.h', + 'scsi_id/scsi_serial.c', +diff --git a/test/fuzz/fuzz-fido-id-desc/crash0 b/test/fuzz/fuzz-fido-id-desc/crash0 +new file mode 100644 +index 0000000000..e066656502 +--- /dev/null ++++ b/test/fuzz/fuzz-fido-id-desc/crash0 +@@ -0,0 +1 @@ ++ +\ No newline at end of file +diff --git a/test/fuzz/fuzz-fido-id-desc/crash1 b/test/fuzz/fuzz-fido-id-desc/crash1 +new file mode 100644 +index 0000000000..aef3e18335 +--- /dev/null ++++ b/test/fuzz/fuzz-fido-id-desc/crash1 +@@ -0,0 +1 @@ ++ +\ No newline at end of file +diff --git a/test/fuzz/fuzz-fido-id-desc/report0 b/test/fuzz/fuzz-fido-id-desc/report0 +new file mode 100644 +index 0000000000000000000000000000000000000000..48757cba682ffddd5a1ddd8988bb8bcdc7db0a7a +GIT binary patch +literal 71 +zcmZQ& +Date: Tue, 27 Aug 2019 19:00:34 +0200 +Subject: [PATCH] shared/but-util: drop trusted annotation from + bus_open_system_watch_bind_with_description() + +https://bugzilla.redhat.com/show_bug.cgi?id=1746057 + +This only affects systemd-resolved. bus_open_system_watch_bind_with_description() +is also used in timesyncd, but it has no methods, only read-only properties, and +in networkd, but it annotates all methods with SD_BUS_VTABLE_UNPRIVILEGED and does +polkit checks. + +Resolves: #1746857 +--- + src/shared/bus-util.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index a4f2deba31..302dbb4c2e 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -1699,10 +1699,6 @@ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *descri + if (r < 0) + return r; + +- r = sd_bus_set_trusted(bus, true); +- if (r < 0) +- return r; +- + r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS); + if (r < 0) + return r; diff --git a/SOURCES/0245-sd-bus-adjust-indentation-of-comments.patch b/SOURCES/0245-sd-bus-adjust-indentation-of-comments.patch new file mode 100644 index 0000000..bdb144e --- /dev/null +++ b/SOURCES/0245-sd-bus-adjust-indentation-of-comments.patch @@ -0,0 +1,50 @@ +From 7e0f9a0cd4053fcc713a99ada3d0d50793b83564 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 27 Aug 2019 19:00:50 +0200 +Subject: [PATCH] sd-bus: adjust indentation of comments + +Related: #1746857 +--- + src/libsystemd/sd-bus/sd-bus.c | 3 +-- + src/shared/bus-util.c | 7 ++++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 3583e24e64..1c9e967ae0 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -1341,8 +1341,7 @@ _public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *descrip + b->bus_client = true; + b->is_user = true; + +- /* We don't do any per-method access control on the user +- * bus. */ ++ /* We don't do any per-method access control on the user bus. */ + b->trusted = true; + b->is_local = true; + +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index 302dbb4c2e..2d908eb45c 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -1675,7 +1675,8 @@ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *descri + + assert(ret); + +- /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal turned on. */ ++ /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal ++ * turned on. */ + + r = sd_bus_new(&bus); + if (r < 0) +@@ -1890,8 +1891,8 @@ int bus_reply_pair_array(sd_bus_message *m, char **l) { + + assert(m); + +- /* Reply to the specified message with a message containing a dictionary put together from the specified +- * strv */ ++ /* Reply to the specified message with a message containing a dictionary put together from the ++ * specified strv */ + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) diff --git a/SOURCES/0246-resolved-do-not-run-loop-twice.patch b/SOURCES/0246-resolved-do-not-run-loop-twice.patch new file mode 100644 index 0000000..70fdf78 --- /dev/null +++ b/SOURCES/0246-resolved-do-not-run-loop-twice.patch @@ -0,0 +1,46 @@ +From d95afbca80cf52f0bc84b2e1b4af6aadda007138 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 27 Aug 2019 19:02:53 +0200 +Subject: [PATCH] resolved: do not run loop twice + +This doesn't matter much, but let's just do the loop once and allocate +the populate the result set on the fly. If we find an error, it'll get +cleaned up automatically. + +Related: #1746857 +--- + src/resolve/resolved-link-bus.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c +index b1581740d8..46d2b11636 100644 +--- a/src/resolve/resolved-link-bus.c ++++ b/src/resolve/resolved-link-bus.c +@@ -492,6 +492,10 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v + if (r < 0) + return r; + ++ ns = set_new(&dns_name_hash_ops); ++ if (!ns) ++ return -ENOMEM; ++ + r = sd_bus_message_read_strv(message, &ntas); + if (r < 0) + return r; +@@ -501,14 +505,9 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v + if (r < 0) + return r; + if (r == 0) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i); +- } ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, ++ "Invalid negative trust anchor domain: %s", *i); + +- ns = set_new(&dns_name_hash_ops); +- if (!ns) +- return -ENOMEM; +- +- STRV_FOREACH(i, ntas) { + r = set_put_strdup(ns, *i); + if (r < 0) + return r; diff --git a/SOURCES/0247-resolved-allow-access-to-Set-Link-and-Revert-methods.patch b/SOURCES/0247-resolved-allow-access-to-Set-Link-and-Revert-methods.patch new file mode 100644 index 0000000..5e2b359 --- /dev/null +++ b/SOURCES/0247-resolved-allow-access-to-Set-Link-and-Revert-methods.patch @@ -0,0 +1,347 @@ +From ddd08e75b1e7fa1f6dfef3d30a0c1ef8c63e4d07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 27 Aug 2019 19:25:05 +0200 +Subject: [PATCH] resolved: allow access to Set*Link and Revert methods through + polkit + +This matches what is done in networkd very closely. In fact even the +policy descriptions are all identical (with s/network/resolve), except +for the last one: +resolved has org.freedesktop.resolve1.revert while +networkd has org.freedesktop.network1.revert-ntp and +org.freedesktop.network1.revert-dns so the description is a bit different. + +Conflicts: + src/resolve/resolved-bus.c + src/resolve/resolved-link-bus.c + +Related: #1746857 +--- + src/resolve/org.freedesktop.resolve1.policy | 99 +++++++++++++++++++++ + src/resolve/resolved-bus.c | 22 ++--- + src/resolve/resolved-link-bus.c | 97 +++++++++++++++++--- + 3 files changed, 197 insertions(+), 21 deletions(-) + +diff --git a/src/resolve/org.freedesktop.resolve1.policy b/src/resolve/org.freedesktop.resolve1.policy +index b65ba3e56a..592c4eb8b0 100644 +--- a/src/resolve/org.freedesktop.resolve1.policy ++++ b/src/resolve/org.freedesktop.resolve1.policy +@@ -40,4 +40,103 @@ + unix-user:systemd-resolve + + ++ ++ Set DNS servers ++ Authentication is required to set DNS servers. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Set domains ++ Authentication is required to set domains. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Set default route ++ Authentication is required to set default route. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Enable/disable LLMNR ++ Authentication is required to enable or disable LLMNR. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Enable/disable multicast DNS ++ Authentication is required to enable or disable multicast DNS. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Enable/disable DNS over TLS ++ Authentication is required to enable or disable DNS over TLS. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Enable/disable DNSSEC ++ Authentication is required to enable or disable DNSSEC. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Set DNSSEC Negative Trust Anchors ++ Authentication is required to set DNSSEC Negative Trust Anchros. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ ++ ++ Revert name resolution settings ++ Authentication is required to revert name resolution settings. ++ ++ auth_admin ++ auth_admin ++ auth_admin_keep ++ ++ unix-user:systemd-resolve ++ ++ + +diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c +index da0a909dd6..4d6cc4fd48 100644 +--- a/src/resolve/resolved-bus.c ++++ b/src/resolve/resolved-bus.c +@@ -1848,18 +1848,18 @@ static const sd_bus_vtable resolve_vtable[] = { + SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0), +- SD_BUS_METHOD("FlushCaches", NULL, NULL, bus_method_flush_caches, 0), +- SD_BUS_METHOD("ResetServerFeatures", NULL, NULL, bus_method_reset_server_features, 0), ++ SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("FlushCaches", NULL, NULL, bus_method_flush_caches, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("ResetServerFeatures", NULL, NULL, bus_method_reset_server_features, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED), +- SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0), +- SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0), +- SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0), +- SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0), +- SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, 0), +- SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0), +- SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0), +- SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0), ++ SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_METHOD("RegisterService", "sssqqqaa{say}", "o", bus_method_register_service, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnregisterService", "o", NULL, bus_method_unregister_service, SD_BUS_VTABLE_UNPRIVILEGED), +diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c +index 46d2b11636..bf3e42264e 100644 +--- a/src/resolve/resolved-link-bus.c ++++ b/src/resolve/resolved-link-bus.c +@@ -1,5 +1,9 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + ++#include ++#include ++#include ++ + #include "alloc-util.h" + #include "bus-common-errors.h" + #include "bus-util.h" +@@ -9,6 +13,7 @@ + #include "resolved-link-bus.h" + #include "resolved-resolv-conf.h" + #include "strv.h" ++#include "user-util.h" + + static BUS_DEFINE_PROPERTY_GET(property_get_dnssec_supported, "b", Link, link_dnssec_supported); + static BUS_DEFINE_PROPERTY_GET2(property_get_dnssec_mode, "s", Link, link_get_dnssec_mode, dnssec_mode_to_string); +@@ -235,6 +240,15 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_ + if (r < 0) + return r; + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-dns-servers", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + dns_server_mark_all(l->dns_servers); + + for (i = 0; i < n; i++) { +@@ -298,12 +312,21 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain"); + } + +- dns_search_domain_mark_all(l->search_domains); +- + r = sd_bus_message_rewind(message, false); + if (r < 0) + return r; + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-domains", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ ++ dns_search_domain_mark_all(l->search_domains); ++ + for (;;) { + DnsSearchDomain *d; + const char *name; +@@ -371,6 +394,15 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr); + } + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-llmnr", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + l->llmnr_support = mode; + link_allocate_scopes(l); + link_add_rrs(l, false); +@@ -405,6 +437,15 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns); + } + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-mdns", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + l->mdns_support = mode; + link_allocate_scopes(l); + link_add_rrs(l, false); +@@ -439,6 +480,15 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls); + } + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-dns-over-tls", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + link_set_dns_over_tls_mode(l, mode); + + (void) link_save_user(l); +@@ -471,6 +521,15 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec); + } + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-dnssec", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + link_set_dnssec_mode(l, mode); + + (void) link_save_user(l); +@@ -513,6 +572,15 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v + return r; + } + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + set_free_free(l->dnssec_negative_trust_anchors); + l->dnssec_negative_trust_anchors = TAKE_PTR(ns); + +@@ -532,6 +600,15 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error + if (r < 0) + return r; + ++ r = bus_verify_polkit_async(message, CAP_NET_ADMIN, ++ "org.freedesktop.resolve1.revert", ++ NULL, true, UID_INVALID, ++ &l->manager->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + link_flush_settings(l); + link_allocate_scopes(l); + link_add_rrs(l, false); +@@ -556,14 +633,14 @@ const sd_bus_vtable link_vtable[] = { + SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), + SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0), + +- SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0), +- SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0), +- SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0), +- SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0), +- SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, 0), +- SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0), +- SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, 0), +- SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, 0), ++ SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, SD_BUS_VTABLE_UNPRIVILEGED), + + SD_BUS_VTABLE_END + }; diff --git a/SOURCES/0248-resolved-query-polkit-only-after-parsing-the-data.patch b/SOURCES/0248-resolved-query-polkit-only-after-parsing-the-data.patch new file mode 100644 index 0000000..eac8530 --- /dev/null +++ b/SOURCES/0248-resolved-query-polkit-only-after-parsing-the-data.patch @@ -0,0 +1,48 @@ +From 7b00cae817e54ee3398ad3b42ec69a3b63676562 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 27 Aug 2019 19:28:19 +0200 +Subject: [PATCH] resolved: query polkit only after parsing the data + +That's what we do everywhere else because it leads to nicer user experience. + +Related: #1746857 +--- + src/resolve/resolved-bus.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c +index 4d6cc4fd48..3f6a6f9e12 100644 +--- a/src/resolve/resolved-bus.c ++++ b/src/resolve/resolved-bus.c +@@ -1632,15 +1632,6 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata, + if (m->mdns_support != RESOLVE_SUPPORT_YES) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for MulticastDNS is disabled"); + +- r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, +- "org.freedesktop.resolve1.register-service", +- NULL, false, UID_INVALID, +- &m->polkit_registry, error); +- if (r < 0) +- return r; +- if (r == 0) +- return 1; /* Polkit will call us back */ +- + service = new0(DnssdService, 1); + if (!service) + return log_oom(); +@@ -1765,6 +1756,15 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata, + if (r < 0) + return r; + ++ r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, ++ "org.freedesktop.resolve1.register-service", ++ NULL, false, UID_INVALID, ++ &m->polkit_registry, error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Polkit will call us back */ ++ + r = hashmap_ensure_allocated(&m->dnssd_services, &string_hash_ops); + if (r < 0) + return r; diff --git a/SOURCES/0249-journal-rely-on-_cleanup_free_-to-free-a-temporary-s.patch b/SOURCES/0249-journal-rely-on-_cleanup_free_-to-free-a-temporary-s.patch new file mode 100644 index 0000000..932a2fc --- /dev/null +++ b/SOURCES/0249-journal-rely-on-_cleanup_free_-to-free-a-temporary-s.patch @@ -0,0 +1,48 @@ +From 4d2145e3edd6ba6ac2e52a232fa5059ecdacaead Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 24 Dec 2018 00:29:56 +0100 +Subject: [PATCH] journal: rely on _cleanup_free_ to free a temporary string + used in client_context_read_cgroup + +Closes https://github.com/systemd/systemd/issues/11253. + +(cherry picked from commit ef30f7cac18a810814ada7e6a68a31d48cc9fccd) + +Resolves: #1764560 +--- + src/journal/journald-context.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c +index c8e97e16de..3a768094d9 100644 +--- a/src/journal/journald-context.c ++++ b/src/journal/journald-context.c +@@ -282,7 +282,7 @@ static int client_context_read_label( + } + + static int client_context_read_cgroup(Server *s, ClientContext *c, const char *unit_id) { +- char *t = NULL; ++ _cleanup_free_ char *t = NULL; + int r; + + assert(c); +@@ -290,7 +290,6 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u + /* Try to acquire the current cgroup path */ + r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t); + if (r < 0 || empty_or_root(t)) { +- + /* We use the unit ID passed in as fallback if we have nothing cached yet and cg_pid_get_path_shifted() + * failed or process is running in a root cgroup. Zombie processes are automatically migrated to root cgroup + * on cgroupsv1 and we want to be able to map log messages from them too. */ +@@ -304,10 +303,8 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u + } + + /* Let's shortcut this if the cgroup path didn't change */ +- if (streq_ptr(c->cgroup, t)) { +- free(t); ++ if (streq_ptr(c->cgroup, t)) + return 0; +- } + + free_and_replace(c->cgroup, t); + diff --git a/SOURCES/0250-basic-user-util-allow-dots-in-user-names.patch b/SOURCES/0250-basic-user-util-allow-dots-in-user-names.patch new file mode 100644 index 0000000..4b03b91 --- /dev/null +++ b/SOURCES/0250-basic-user-util-allow-dots-in-user-names.patch @@ -0,0 +1,90 @@ +From 76176de0889c3e8b9b3a176da24e4f8dbbd380a3 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 2 Oct 2019 11:59:41 +0200 +Subject: [PATCH] basic/user-util: allow dots in user names + +(based on commit 1a29610f5fa1bcb2eeb37d2c6b79d8d1a6dbb865) + +Resolves: #1717603 +--- + src/basic/user-util.c | 9 ++++++--- + src/test/test-user-util.c | 8 ++++---- + 2 files changed, 10 insertions(+), 7 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index c533f67025..d92969c966 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -575,11 +575,14 @@ bool valid_user_group_name(const char *u) { + /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, + * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules: + * +- * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator) + * - We require that names fit into the appropriate utmp field + * - We don't allow empty user names + * + * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. ++ * ++ * jsynacek: We now allow dots in user names. The checks are not exhaustive as user names like "..." are allowed ++ * and valid according to POSIX, but can't be created using useradd. However, ".user" can be created. Let's not ++ * complicate the code by adding additional checks for weird corner cases like these, as they don't really matter here. + */ + + if (isempty(u)) +@@ -587,14 +590,14 @@ bool valid_user_group_name(const char *u) { + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && +- u[0] != '_') ++ u[0] != '_' && u[0] != '.') + return false; + + for (i = u+1; *i; i++) { + if (!(*i >= 'a' && *i <= 'z') && + !(*i >= 'A' && *i <= 'Z') && + !(*i >= '0' && *i <= '9') && +- !IN_SET(*i, '_', '-')) ++ !IN_SET(*i, '_', '-', '.')) + return false; + } + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index c1428fab02..9114d30b8c 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -71,8 +71,6 @@ static void test_valid_user_group_name(void) { + assert_se(!valid_user_group_name("-1")); + assert_se(!valid_user_group_name("-kkk")); + assert_se(!valid_user_group_name("rööt")); +- assert_se(!valid_user_group_name(".")); +- assert_se(!valid_user_group_name("eff.eff")); + assert_se(!valid_user_group_name("foo\nbar")); + assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); +@@ -83,6 +81,8 @@ static void test_valid_user_group_name(void) { + assert_se(valid_user_group_name("_kkk")); + assert_se(valid_user_group_name("kkk-")); + assert_se(valid_user_group_name("kk-k")); ++ assert_se(valid_user_group_name(".moo")); ++ assert_se(valid_user_group_name("eff.eff")); + + assert_se(valid_user_group_name("some5")); + assert_se(!valid_user_group_name("5some")); +@@ -102,8 +102,6 @@ static void test_valid_user_group_name_or_id(void) { + assert_se(!valid_user_group_name_or_id("-1")); + assert_se(!valid_user_group_name_or_id("-kkk")); + assert_se(!valid_user_group_name_or_id("rööt")); +- assert_se(!valid_user_group_name_or_id(".")); +- assert_se(!valid_user_group_name_or_id("eff.eff")); + assert_se(!valid_user_group_name_or_id("foo\nbar")); + assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); +@@ -114,6 +112,8 @@ static void test_valid_user_group_name_or_id(void) { + assert_se(valid_user_group_name_or_id("_kkk")); + assert_se(valid_user_group_name_or_id("kkk-")); + assert_se(valid_user_group_name_or_id("kk-k")); ++ assert_se(valid_user_group_name_or_id(".moo")); ++ assert_se(valid_user_group_name_or_id("eff.eff")); + + assert_se(valid_user_group_name_or_id("some5")); + assert_se(!valid_user_group_name_or_id("5some")); diff --git a/SOURCES/0251-sd-bus-bump-message-queue-size-again.patch b/SOURCES/0251-sd-bus-bump-message-queue-size-again.patch new file mode 100644 index 0000000..5fd7ade --- /dev/null +++ b/SOURCES/0251-sd-bus-bump-message-queue-size-again.patch @@ -0,0 +1,29 @@ +From 62623bafd9ce4842122ddeda83f9527e43b9a21f Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Fri, 8 Nov 2019 14:54:30 +0100 +Subject: [PATCH] sd-bus: bump message queue size again + +Simliarly to issue #4068, the current limit turns out to be too small for a +big storage setup that uses many small disks. Let's bump it further. + +(cherry picked from commit 83a32ea7b03d6707b8e5bb90a0b3a6eb868ef633) +Resolves: #1770189 +--- + src/libsystemd/sd-bus/bus-internal.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h +index 90e6028983..5d773b14c4 100644 +--- a/src/libsystemd/sd-bus/bus-internal.h ++++ b/src/libsystemd/sd-bus/bus-internal.h +@@ -328,8 +328,8 @@ struct sd_bus { + * with enough entropy yet and might delay the boot */ + #define BUS_AUTH_TIMEOUT ((usec_t) DEFAULT_TIMEOUT_USEC) + +-#define BUS_WQUEUE_MAX (192*1024) +-#define BUS_RQUEUE_MAX (192*1024) ++#define BUS_WQUEUE_MAX (384*1024) ++#define BUS_RQUEUE_MAX (384*1024) + + #define BUS_MESSAGE_SIZE_MAX (128*1024*1024) + #define BUS_AUTH_SIZE_MAX (64*1024) diff --git a/SOURCES/0252-tests-put-fuzz_journald_processing_function-in-a-.c-.patch b/SOURCES/0252-tests-put-fuzz_journald_processing_function-in-a-.c-.patch new file mode 100644 index 0000000..532dd60 --- /dev/null +++ b/SOURCES/0252-tests-put-fuzz_journald_processing_function-in-a-.c-.patch @@ -0,0 +1,109 @@ +From 18a45cf91dbdd075fb55d752f959e84d36f3ab3b Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 7 Sep 2018 06:13:17 +0000 +Subject: [PATCH] tests: put fuzz_journald_processing_function in a .c file + +(cherry picked from commit 231dca5579cfba6175d19eee5347d693893fb5aa) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald.c | 30 ++++++++++++++++++++++++++++++ + src/fuzz/fuzz-journald.h | 24 ++---------------------- + src/fuzz/meson.build | 6 ++++-- + 3 files changed, 36 insertions(+), 24 deletions(-) + create mode 100644 src/fuzz/fuzz-journald.c + +diff --git a/src/fuzz/fuzz-journald.c b/src/fuzz/fuzz-journald.c +new file mode 100644 +index 0000000000..f271d7f2fe +--- /dev/null ++++ b/src/fuzz/fuzz-journald.c +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "alloc-util.h" ++#include "fuzz-journald.h" ++#include "journald-server.h" ++#include "sd-event.h" ++ ++void fuzz_journald_processing_function( ++ const uint8_t *data, ++ size_t size, ++ void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) ++ ) { ++ Server s = {}; ++ char *label = NULL; ++ size_t label_len = 0; ++ struct ucred *ucred = NULL; ++ struct timeval *tv = NULL; ++ ++ if (size == 0) ++ return; ++ ++ assert_se(sd_event_default(&s.event) >= 0); ++ s.syslog_fd = s.native_fd = s.stdout_fd = s.dev_kmsg_fd = s.audit_fd = s.hostname_fd = s.notify_fd = -1; ++ s.buffer = memdup_suffix0(data, size); ++ assert_se(s.buffer); ++ s.buffer_size = size + 1; ++ s.storage = STORAGE_NONE; ++ (*f)(&s, s.buffer, size, ucred, tv, label, label_len); ++ server_done(&s); ++} +diff --git a/src/fuzz/fuzz-journald.h b/src/fuzz/fuzz-journald.h +index e66ef54c9b..e9d32a74aa 100644 +--- a/src/fuzz/fuzz-journald.h ++++ b/src/fuzz/fuzz-journald.h +@@ -1,30 +1,10 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + #pragma once + +-#include "alloc-util.h" + #include "journald-server.h" +-#include "sd-event.h" + +-static void fuzz_journald_processing_function( ++void fuzz_journald_processing_function( + const uint8_t *data, + size_t size, + void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) +- ) { +- Server s = {}; +- char *label = NULL; +- size_t label_len = 0; +- struct ucred *ucred = NULL; +- struct timeval *tv = NULL; +- +- if (size == 0) +- return; +- +- assert_se(sd_event_default(&s.event) >= 0); +- s.syslog_fd = s.native_fd = s.stdout_fd = s.dev_kmsg_fd = s.audit_fd = s.hostname_fd = s.notify_fd = -1; +- s.buffer = memdup_suffix0(data, size); +- assert_se(s.buffer); +- s.buffer_size = size + 1; +- s.storage = STORAGE_NONE; +- (*f)(&s, s.buffer, size, ucred, tv, label, label_len); +- server_done(&s); +-} ++); +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 483a952421..1f8631bcc0 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -33,12 +33,14 @@ fuzzers += [ + libshared], + [libmount]], + +- [['src/fuzz/fuzz-journald-native.c'], ++ [['src/fuzz/fuzz-journald-native.c', ++ 'src/fuzz/fuzz-journald.c'], + [libjournal_core, + libshared], + [libselinux]], + +- [['src/fuzz/fuzz-journald-syslog.c'], ++ [['src/fuzz/fuzz-journald-syslog.c', ++ 'src/fuzz/fuzz-journald.c'], + [libjournal_core, + libshared], + [libselinux]], diff --git a/SOURCES/0253-tests-add-a-fuzzer-for-dev_kmsg_record.patch b/SOURCES/0253-tests-add-a-fuzzer-for-dev_kmsg_record.patch new file mode 100644 index 0000000..6e01bbf --- /dev/null +++ b/SOURCES/0253-tests-add-a-fuzzer-for-dev_kmsg_record.patch @@ -0,0 +1,129 @@ +From e7e70f575840cd021f6429f264911ae0cbff9741 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Thu, 15 Nov 2018 17:52:57 +0100 +Subject: [PATCH] tests: add a fuzzer for dev_kmsg_record + +(cherry picked from commit 8857fb9beb9dcb95a6ce1be14dc94c4dc4cd3ea3) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-kmsg.c | 29 +++++++++++++++++++ + src/fuzz/meson.build | 5 ++++ + src/journal/journald-kmsg.c | 2 +- + src/journal/journald-kmsg.h | 2 ++ + test/fuzz/fuzz-journald-kmsg/basic | 1 + + test/fuzz/fuzz-journald-kmsg/dev-null | 2 ++ + test/fuzz/fuzz-journald-kmsg/loopback | 2 ++ + .../fuzz-journald-kmsg/subsystem-loopback | 2 ++ + 8 files changed, 44 insertions(+), 1 deletion(-) + create mode 100644 src/fuzz/fuzz-journald-kmsg.c + create mode 100644 test/fuzz/fuzz-journald-kmsg/basic + create mode 100644 test/fuzz/fuzz-journald-kmsg/dev-null + create mode 100644 test/fuzz/fuzz-journald-kmsg/loopback + create mode 100644 test/fuzz/fuzz-journald-kmsg/subsystem-loopback + +diff --git a/src/fuzz/fuzz-journald-kmsg.c b/src/fuzz/fuzz-journald-kmsg.c +new file mode 100644 +index 0000000000..5d99d244b5 +--- /dev/null ++++ b/src/fuzz/fuzz-journald-kmsg.c +@@ -0,0 +1,29 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "fuzz.h" ++#include "journald-kmsg.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ Server s = {}; ++ _cleanup_free_ char *buffer = NULL; ++ ++ if (size == 0) ++ return 0; ++ ++ s = (Server) { ++ .native_fd = -1, ++ .stdout_fd = -1, ++ .dev_kmsg_fd = -1, ++ .audit_fd = -1, ++ .hostname_fd = -1, ++ .notify_fd = -1, ++ .storage = STORAGE_NONE, ++ }; ++ assert_se(sd_event_default(&s.event) >= 0); ++ buffer = memdup(data, size); ++ assert_se(buffer); ++ dev_kmsg_record(&s, buffer, size); ++ server_done(&s); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 1f8631bcc0..0520e448a9 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -33,6 +33,11 @@ fuzzers += [ + libshared], + [libmount]], + ++ [['src/fuzz/fuzz-journald-kmsg.c'], ++ [libjournal_core, ++ libshared], ++ [libselinux]], ++ + [['src/fuzz/fuzz-journald-native.c', + 'src/fuzz/fuzz-journald.c'], + [libjournal_core, +diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c +index 7644bebfc8..0cdf1c4794 100644 +--- a/src/journal/journald-kmsg.c ++++ b/src/journal/journald-kmsg.c +@@ -93,7 +93,7 @@ static bool is_us(const char *identifier, const char *pid) { + streq(identifier, program_invocation_short_name); + } + +-static void dev_kmsg_record(Server *s, char *p, size_t l) { ++void dev_kmsg_record(Server *s, char *p, size_t l) { + + _cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL; + struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; +diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h +index bff24ac310..2326bc8c93 100644 +--- a/src/journal/journald-kmsg.h ++++ b/src/journal/journald-kmsg.h +@@ -9,3 +9,5 @@ int server_flush_dev_kmsg(Server *s); + void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); + + int server_open_kernel_seqnum(Server *s); ++ ++void dev_kmsg_record(Server *s, char *p, size_t l); +diff --git a/test/fuzz/fuzz-journald-kmsg/basic b/test/fuzz/fuzz-journald-kmsg/basic +new file mode 100644 +index 0000000000..1299cd0869 +--- /dev/null ++++ b/test/fuzz/fuzz-journald-kmsg/basic +@@ -0,0 +1 @@ ++29,456,292891883,-;systemd[1]: Reexecuting. +diff --git a/test/fuzz/fuzz-journald-kmsg/dev-null b/test/fuzz/fuzz-journald-kmsg/dev-null +new file mode 100644 +index 0000000000..de039588b5 +--- /dev/null ++++ b/test/fuzz/fuzz-journald-kmsg/dev-null +@@ -0,0 +1,2 @@ ++12,460,1322026586,-;hey ++ DEVICE=c1:3 +diff --git a/test/fuzz/fuzz-journald-kmsg/loopback b/test/fuzz/fuzz-journald-kmsg/loopback +new file mode 100644 +index 0000000000..ca320177b7 +--- /dev/null ++++ b/test/fuzz/fuzz-journald-kmsg/loopback +@@ -0,0 +1,2 @@ ++12,460,1322026586,-;hey ++ DEVICE=n1 +diff --git a/test/fuzz/fuzz-journald-kmsg/subsystem-loopback b/test/fuzz/fuzz-journald-kmsg/subsystem-loopback +new file mode 100644 +index 0000000000..af9c0d91e5 +--- /dev/null ++++ b/test/fuzz/fuzz-journald-kmsg/subsystem-loopback +@@ -0,0 +1,2 @@ ++12,460,1322026586,-;hey ++ DEVICE=+net:lo diff --git a/SOURCES/0254-basic-remove-an-assertion-from-cunescape_one.patch b/SOURCES/0254-basic-remove-an-assertion-from-cunescape_one.patch new file mode 100644 index 0000000..ec38ffc --- /dev/null +++ b/SOURCES/0254-basic-remove-an-assertion-from-cunescape_one.patch @@ -0,0 +1,30 @@ +From 43d72623fdfca8500c8c89a4b5023e35a3f0b259 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 16 Nov 2018 07:05:29 +0100 +Subject: [PATCH] basic: remove an assertion from cunescape_one + +The function takes a pointer to a random block of memory and +the length of that block. It shouldn't crash every time it sees +a zero byte at the beginning there. + +This should help the dev-kmsg fuzzer to keep going. + +(cherry picked from commit 8dc4de966ce6d32470aaff30ed054f6a2688d6d7) + +Resolves: #1764560 +--- + src/basic/escape.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/basic/escape.c b/src/basic/escape.c +index 5004763d97..5f715156fb 100644 +--- a/src/basic/escape.c ++++ b/src/basic/escape.c +@@ -106,7 +106,6 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) + int r = 1; + + assert(p); +- assert(*p); + assert(ret); + + /* Unescapes C style. Returns the unescaped character in ret. diff --git a/SOURCES/0255-journal-fix-an-off-by-one-error-in-dev_kmsg_record.patch b/SOURCES/0255-journal-fix-an-off-by-one-error-in-dev_kmsg_record.patch new file mode 100644 index 0000000..28a0b3e --- /dev/null +++ b/SOURCES/0255-journal-fix-an-off-by-one-error-in-dev_kmsg_record.patch @@ -0,0 +1,25 @@ +From f00d221d5dc92a530e260db5f44fa57653f03e8b Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 16 Nov 2018 07:11:06 +0100 +Subject: [PATCH] journal: fix an off-by-one error in dev_kmsg_record + +(cherry picked from commit 080d112caa0dc948555a69a008c1caf4d5d41ed6) + +Resolves: #1764560 +--- + src/journal/journald-kmsg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c +index 0cdf1c4794..726c006ce1 100644 +--- a/src/journal/journald-kmsg.c ++++ b/src/journal/journald-kmsg.c +@@ -239,7 +239,7 @@ void dev_kmsg_record(Server *s, char *p, size_t l) { + ll = udev_device_get_devlinks_list_entry(ud); + udev_list_entry_foreach(ll, ll) { + +- if (j > N_IOVEC_UDEV_FIELDS) ++ if (j >= N_IOVEC_UDEV_FIELDS) + break; + + g = udev_list_entry_get_name(ll); diff --git a/SOURCES/0256-tests-add-a-reproducer-for-a-memory-leak-fixed-in-30.patch b/SOURCES/0256-tests-add-a-reproducer-for-a-memory-leak-fixed-in-30.patch new file mode 100644 index 0000000..14c1223 --- /dev/null +++ b/SOURCES/0256-tests-add-a-reproducer-for-a-memory-leak-fixed-in-30.patch @@ -0,0 +1,25 @@ +From 4caa887ae8eabc087d6f9f2b233c96c220de8b02 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 16 Nov 2018 07:20:44 +0100 +Subject: [PATCH] tests: add a reproducer for a memory leak fixed in + 30eddcd51b8a472e05d3b8d1 in August + +(cherry picked from commit 1dd485b700fe9ad94d7a780f14fcf18a4738ace4) + +Resolves: #1764560 +--- + ...leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43 | Bin 0 -> 1847 bytes + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43 + +diff --git a/test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43 b/test/fuzz/fuzz-journald-kmsg/leak-ab161e601e82f1ec31d11e2cbae2747834ce9e43 +new file mode 100644 +index 0000000000000000000000000000000000000000..424ae5cb010aa519758e6af90cc981795b68d3fd +GIT binary patch +literal 1847 +zcmXps(lIeJ&@nVNGBPkSGqo_&(Y4MjX0aSiiycD2<{NiEaQE6vG)izFLb8I! +Date: Fri, 16 Nov 2018 07:33:02 +0100 +Subject: [PATCH] tests: add a reproducer for a heap-buffer-overflow fixed in + 937b1171378bc1000a + +(cherry picked from commit f7a6b40187a98751a9ab6867e8b52e4e6f1dad5c) + +Resolves: #1764560 +--- + ...crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963 | Bin 0 -> 112 bytes + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963 + +diff --git a/test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963 b/test/fuzz/fuzz-journald-kmsg/crash-c6c04d83e73f3d1417bc0afce8fa81b99f955963 +new file mode 100644 +index 0000000000000000000000000000000000000000..19887a1fec9fc29b1f7da8a2d1c5ea5054f2bc02 +GIT binary patch +literal 112 +zcmXpq)Zrxx80r}680lCOP-~&{)k?wIfGehgOM!tQroxI#0Z63Aa4DF?03ibx03hxS +A82|tP + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0258-test-initialize-syslog_fd-in-fuzz-journald-kmsg-too.patch b/SOURCES/0258-test-initialize-syslog_fd-in-fuzz-journald-kmsg-too.patch new file mode 100644 index 0000000..3ffa913 --- /dev/null +++ b/SOURCES/0258-test-initialize-syslog_fd-in-fuzz-journald-kmsg-too.patch @@ -0,0 +1,44 @@ +From af471e7402a70b670cd50e45c6139a0ac50a74bd Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 16 Nov 2018 09:23:53 +0100 +Subject: [PATCH] test: initialize syslog_fd in fuzz-journald-kmsg too + +This is a follow-up to 8857fb9beb9dcb that prevents the fuzzer from crashing with +``` +==220==ERROR: AddressSanitizer: ABRT on unknown address 0x0000000000dc (pc 0x7ff4953c8428 bp 0x7ffcf66ec290 sp 0x7ffcf66ec128 T0) +SCARINESS: 10 (signal) + #0 0x7ff4953c8427 in gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x35427) + #1 0x7ff4953ca029 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x37029) + #2 0x7ff49666503a in log_assert_failed_realm /work/build/../../src/systemd/src/basic/log.c:805:9 + #3 0x7ff496614ecf in safe_close /work/build/../../src/systemd/src/basic/fd-util.c:66:17 + #4 0x548806 in server_done /work/build/../../src/systemd/src/journal/journald-server.c:2064:9 + #5 0x5349fa in LLVMFuzzerTestOneInput /work/build/../../src/systemd/src/fuzz/fuzz-journald-kmsg.c:26:9 + #6 0x592755 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/libfuzzer/FuzzerLoop.cpp:571:15 + #7 0x590627 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /src/libfuzzer/FuzzerLoop.cpp:480:3 + #8 0x594432 in fuzzer::Fuzzer::MutateAndTestOne() /src/libfuzzer/FuzzerLoop.cpp:708:19 + #9 0x5973c6 in fuzzer::Fuzzer::Loop(std::__1::vector, std::__1::allocator >, fuzzer::fuzzer_allocator, std::__1::allocator > > > const&) /src/libfuzzer/FuzzerLoop.cpp:839:5 + #10 0x574541 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/libfuzzer/FuzzerDriver.cpp:764:6 + #11 0x5675fc in main /src/libfuzzer/FuzzerMain.cpp:20:10 + #12 0x7ff4953b382f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) + #13 0x420f58 in _start (/out/fuzz-journald-kmsg+0x420f58) +``` + +(cherry picked from commit cc55ac0171a2493768c021faa356513642797e7f) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-kmsg.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/fuzz/fuzz-journald-kmsg.c b/src/fuzz/fuzz-journald-kmsg.c +index 5d99d244b5..e2611c6d45 100644 +--- a/src/fuzz/fuzz-journald-kmsg.c ++++ b/src/fuzz/fuzz-journald-kmsg.c +@@ -11,6 +11,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + return 0; + + s = (Server) { ++ .syslog_fd = -1, + .native_fd = -1, + .stdout_fd = -1, + .dev_kmsg_fd = -1, diff --git a/SOURCES/0259-tests-add-a-fuzzer-for-process_audit_string.patch b/SOURCES/0259-tests-add-a-fuzzer-for-process_audit_string.patch new file mode 100644 index 0000000..d6ea02d --- /dev/null +++ b/SOURCES/0259-tests-add-a-fuzzer-for-process_audit_string.patch @@ -0,0 +1,99 @@ +From f991a9c7644f3fb5155ff823600ba5a6ea403dc4 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 16 Nov 2018 21:23:56 +0100 +Subject: [PATCH] tests: add a fuzzer for process_audit_string + +(cherry picked from commit 090a20cfaf3d5439fa39c5d8df473b0cfef181dd) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-audit.c | 27 +++++++++++++++++++++++++++ + src/fuzz/meson.build | 5 +++++ + src/journal/journald-audit.c | 2 +- + src/journal/journald-audit.h | 2 ++ + test/fuzz/fuzz-journald-audit/basic | 1 + + 5 files changed, 36 insertions(+), 1 deletion(-) + create mode 100644 src/fuzz/fuzz-journald-audit.c + create mode 100644 test/fuzz/fuzz-journald-audit/basic + +diff --git a/src/fuzz/fuzz-journald-audit.c b/src/fuzz/fuzz-journald-audit.c +new file mode 100644 +index 0000000000..fe401c0d98 +--- /dev/null ++++ b/src/fuzz/fuzz-journald-audit.c +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "fuzz.h" ++#include "journald-audit.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ Server s; ++ _cleanup_free_ char *buffer = NULL; ++ ++ s = (Server) { ++ .syslog_fd = -1, ++ .native_fd = -1, ++ .stdout_fd = -1, ++ .dev_kmsg_fd = -1, ++ .audit_fd = -1, ++ .hostname_fd = -1, ++ .notify_fd = -1, ++ .storage = STORAGE_NONE, ++ }; ++ assert_se(sd_event_default(&s.event) >= 0); ++ buffer = memdup_suffix0(data, size); ++ assert_se(buffer); ++ process_audit_string(&s, 0, buffer, size); ++ server_done(&s); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 0520e448a9..5548da3822 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -33,6 +33,11 @@ fuzzers += [ + libshared], + [libmount]], + ++ [['src/fuzz/fuzz-journald-audit.c'], ++ [libjournal_core, ++ libshared], ++ [libselinux]], ++ + [['src/fuzz/fuzz-journald-kmsg.c'], + [libjournal_core, + libshared], +diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c +index 87726684af..7810a0139a 100644 +--- a/src/journal/journald-audit.c ++++ b/src/journal/journald-audit.c +@@ -313,7 +313,7 @@ static int map_all_fields( + } + } + +-static void process_audit_string(Server *s, int type, const char *data, size_t size) { ++void process_audit_string(Server *s, int type, const char *data, size_t size) { + size_t n_iov_allocated = 0, n_iov = 0, z; + _cleanup_free_ struct iovec *iov = NULL; + uint64_t seconds, msec, id; +diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h +index 57bb1711c9..7766618c98 100644 +--- a/src/journal/journald-audit.h ++++ b/src/journal/journald-audit.h +@@ -6,4 +6,6 @@ + + void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen); + ++void process_audit_string(Server *s, int type, const char *data, size_t size); ++ + int server_open_audit(Server*s); +diff --git a/test/fuzz/fuzz-journald-audit/basic b/test/fuzz/fuzz-journald-audit/basic +new file mode 100644 +index 0000000000..d1ce8cc5f0 +--- /dev/null ++++ b/test/fuzz/fuzz-journald-audit/basic +@@ -0,0 +1 @@ ++audit(1542398162.211:744): pid=7376 uid=1000 auid=1000 ses=6 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="vagrant" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success' +\ No newline at end of file diff --git a/SOURCES/0260-journald-check-whether-sscanf-has-changed-the-value-.patch b/SOURCES/0260-journald-check-whether-sscanf-has-changed-the-value-.patch new file mode 100644 index 0000000..29ce46f --- /dev/null +++ b/SOURCES/0260-journald-check-whether-sscanf-has-changed-the-value-.patch @@ -0,0 +1,47 @@ +From bef599d1a0e41afe4b6f1d6dfb6fbc86896ab8c5 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 16 Nov 2018 23:32:31 +0100 +Subject: [PATCH] journald: check whether sscanf has changed the value + corresponding to %n + +It's possible for sscanf to receive strings containing all three fields +and not matching the template at the same time. When this happens the +value of k doesn't change, which basically means that process_audit_string +tries to access memory randomly. Sometimes it works and sometimes it doesn't :-) + +See also https://bugzilla.redhat.com/show_bug.cgi?id=1059314. + +(cherry picked from commit 1dab14aba749b9c5ab8176c5730107b70834240b) + +Resolves: #1764560 +--- + src/journal/journald-audit.c | 3 ++- + test/fuzz/fuzz-journald-audit/crash | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + create mode 100644 test/fuzz/fuzz-journald-audit/crash + +diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c +index 7810a0139a..0fd6ab2a84 100644 +--- a/src/journal/journald-audit.c ++++ b/src/journal/journald-audit.c +@@ -341,11 +341,12 @@ void process_audit_string(Server *s, int type, const char *data, size_t size) { + if (!p) + return; + ++ k = 0; + if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n", + &seconds, + &msec, + &id, +- &k) != 3) ++ &k) != 3 || k == 0) + return; + + p += k; +diff --git a/test/fuzz/fuzz-journald-audit/crash b/test/fuzz/fuzz-journald-audit/crash +new file mode 100644 +index 0000000000..91bd85ca6e +--- /dev/null ++++ b/test/fuzz/fuzz-journald-audit/crash +@@ -0,0 +1 @@ ++audit(1542398162.211:744) pid=7376 uid=1000 auid=1000 ses=6 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 msg='op=PAM:accounting grantors=pam_unix,pam_localuser acct="vagrant" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success' diff --git a/SOURCES/0261-tests-introduce-dummy_server_init-and-use-it-in-all-.patch b/SOURCES/0261-tests-introduce-dummy_server_init-and-use-it-in-all-.patch new file mode 100644 index 0000000..f4a5225 --- /dev/null +++ b/SOURCES/0261-tests-introduce-dummy_server_init-and-use-it-in-all-.patch @@ -0,0 +1,172 @@ +From b276c85200786add6c86b6c1fedc888c71ffe5db Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Sat, 17 Nov 2018 13:01:09 +0100 +Subject: [PATCH] tests: introduce dummy_server_init and use it in all journald + fuzzers + +(cherry picked from commit ed62712dc6fb236845c489a7f386c7aff0ec31d6) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-audit.c | 18 +++--------------- + src/fuzz/fuzz-journald-kmsg.c | 20 ++++---------------- + src/fuzz/fuzz-journald.c | 26 +++++++++++++++++++------- + src/fuzz/fuzz-journald.h | 2 ++ + src/fuzz/meson.build | 6 ++++-- + 5 files changed, 32 insertions(+), 40 deletions(-) + +diff --git a/src/fuzz/fuzz-journald-audit.c b/src/fuzz/fuzz-journald-audit.c +index fe401c0d98..3f3ce7e8ee 100644 +--- a/src/fuzz/fuzz-journald-audit.c ++++ b/src/fuzz/fuzz-journald-audit.c +@@ -1,26 +1,14 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + + #include "fuzz.h" ++#include "fuzz-journald.h" + #include "journald-audit.h" + + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + Server s; +- _cleanup_free_ char *buffer = NULL; + +- s = (Server) { +- .syslog_fd = -1, +- .native_fd = -1, +- .stdout_fd = -1, +- .dev_kmsg_fd = -1, +- .audit_fd = -1, +- .hostname_fd = -1, +- .notify_fd = -1, +- .storage = STORAGE_NONE, +- }; +- assert_se(sd_event_default(&s.event) >= 0); +- buffer = memdup_suffix0(data, size); +- assert_se(buffer); +- process_audit_string(&s, 0, buffer, size); ++ dummy_server_init(&s, data, size); ++ process_audit_string(&s, 0, s.buffer, size); + server_done(&s); + + return 0; +diff --git a/src/fuzz/fuzz-journald-kmsg.c b/src/fuzz/fuzz-journald-kmsg.c +index e2611c6d45..f7426c8400 100644 +--- a/src/fuzz/fuzz-journald-kmsg.c ++++ b/src/fuzz/fuzz-journald-kmsg.c +@@ -1,29 +1,17 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + + #include "fuzz.h" ++#include "fuzz-journald.h" + #include "journald-kmsg.h" + + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { +- Server s = {}; +- _cleanup_free_ char *buffer = NULL; ++ Server s; + + if (size == 0) + return 0; + +- s = (Server) { +- .syslog_fd = -1, +- .native_fd = -1, +- .stdout_fd = -1, +- .dev_kmsg_fd = -1, +- .audit_fd = -1, +- .hostname_fd = -1, +- .notify_fd = -1, +- .storage = STORAGE_NONE, +- }; +- assert_se(sd_event_default(&s.event) >= 0); +- buffer = memdup(data, size); +- assert_se(buffer); +- dev_kmsg_record(&s, buffer, size); ++ dummy_server_init(&s, data, size); ++ dev_kmsg_record(&s, s.buffer, size); + server_done(&s); + + return 0; +diff --git a/src/fuzz/fuzz-journald.c b/src/fuzz/fuzz-journald.c +index f271d7f2fe..0659b92ba3 100644 +--- a/src/fuzz/fuzz-journald.c ++++ b/src/fuzz/fuzz-journald.c +@@ -5,12 +5,29 @@ + #include "journald-server.h" + #include "sd-event.h" + ++void dummy_server_init(Server *s, const uint8_t *buffer, size_t size) { ++ *s = (Server) { ++ .syslog_fd = -1, ++ .native_fd = -1, ++ .stdout_fd = -1, ++ .dev_kmsg_fd = -1, ++ .audit_fd = -1, ++ .hostname_fd = -1, ++ .notify_fd = -1, ++ .storage = STORAGE_NONE, ++ }; ++ assert_se(sd_event_default(&s->event) >= 0); ++ s->buffer = memdup_suffix0(buffer, size); ++ assert_se(s->buffer); ++ s->buffer_size = size + 1; ++} ++ + void fuzz_journald_processing_function( + const uint8_t *data, + size_t size, + void (*f)(Server *s, const char *buf, size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) + ) { +- Server s = {}; ++ Server s; + char *label = NULL; + size_t label_len = 0; + struct ucred *ucred = NULL; +@@ -19,12 +36,7 @@ void fuzz_journald_processing_function( + if (size == 0) + return; + +- assert_se(sd_event_default(&s.event) >= 0); +- s.syslog_fd = s.native_fd = s.stdout_fd = s.dev_kmsg_fd = s.audit_fd = s.hostname_fd = s.notify_fd = -1; +- s.buffer = memdup_suffix0(data, size); +- assert_se(s.buffer); +- s.buffer_size = size + 1; +- s.storage = STORAGE_NONE; ++ dummy_server_init(&s, data, size); + (*f)(&s, s.buffer, size, ucred, tv, label, label_len); + server_done(&s); + } +diff --git a/src/fuzz/fuzz-journald.h b/src/fuzz/fuzz-journald.h +index e9d32a74aa..77e3b0c064 100644 +--- a/src/fuzz/fuzz-journald.h ++++ b/src/fuzz/fuzz-journald.h +@@ -3,6 +3,8 @@ + + #include "journald-server.h" + ++void dummy_server_init(Server *s, const uint8_t *buffer, size_t size); ++ + void fuzz_journald_processing_function( + const uint8_t *data, + size_t size, +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 5548da3822..897c02e4ae 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -33,12 +33,14 @@ fuzzers += [ + libshared], + [libmount]], + +- [['src/fuzz/fuzz-journald-audit.c'], ++ [['src/fuzz/fuzz-journald-audit.c', ++ 'src/fuzz/fuzz-journald.c'], + [libjournal_core, + libshared], + [libselinux]], + +- [['src/fuzz/fuzz-journald-kmsg.c'], ++ [['src/fuzz/fuzz-journald-kmsg.c', ++ 'src/fuzz/fuzz-journald.c'], + [libjournal_core, + libshared], + [libselinux]], diff --git a/SOURCES/0262-tests-add-a-fuzzer-for-journald-streams.patch b/SOURCES/0262-tests-add-a-fuzzer-for-journald-streams.patch new file mode 100644 index 0000000..03ff6e8 --- /dev/null +++ b/SOURCES/0262-tests-add-a-fuzzer-for-journald-streams.patch @@ -0,0 +1,148 @@ +From e7077e3a551a3faedfcc3d007de6a72fb5e1df62 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Tue, 20 Nov 2018 01:20:32 +0100 +Subject: [PATCH] tests: add a fuzzer for journald streams + +(cherry picked from commit 9541f5ff5c637bb1b3e3c69706cb73e68ff06813) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-stream.c | 35 ++++++++++++++++++++++++++++ + src/fuzz/fuzz-journald.c | 10 +++++--- + src/fuzz/meson.build | 6 +++++ + src/journal/journald-stream.c | 4 ++-- + src/journal/journald-stream.h | 2 ++ + test/fuzz/fuzz-journald-stream/basic | 8 +++++++ + 6 files changed, 60 insertions(+), 5 deletions(-) + create mode 100644 src/fuzz/fuzz-journald-stream.c + create mode 100644 test/fuzz/fuzz-journald-stream/basic + +diff --git a/src/fuzz/fuzz-journald-stream.c b/src/fuzz/fuzz-journald-stream.c +new file mode 100644 +index 0000000000..247c0889bc +--- /dev/null ++++ b/src/fuzz/fuzz-journald-stream.c +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++ ++#include "fd-util.h" ++#include "fuzz.h" ++#include "fuzz-journald.h" ++#include "journald-stream.h" ++ ++static int stream_fds[2] = { -1, -1 }; ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ Server s; ++ StdoutStream *stream; ++ int v; ++ ++ if (size == 0) ++ return 0; ++ ++ if (!getenv("SYSTEMD_LOG_LEVEL")) ++ log_set_max_level(LOG_CRIT); ++ ++ assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0, stream_fds) >= 0); ++ dummy_server_init(&s, NULL, 0); ++ assert_se(stdout_stream_install(&s, stream_fds[0], &stream) >= 0); ++ assert_se(write(stream_fds[1], data, size) == (ssize_t) size); ++ while (ioctl(stream_fds[0], SIOCINQ, &v) == 0 && v) ++ sd_event_run(s.event, (uint64_t) -1); ++ if (s.n_stdout_streams) ++ stdout_stream_destroy(stream); ++ server_done(&s); ++ stream_fds[1] = safe_close(stream_fds[1]); ++ ++ return 0; ++} +diff --git a/src/fuzz/fuzz-journald.c b/src/fuzz/fuzz-journald.c +index 0659b92ba3..950e885cae 100644 +--- a/src/fuzz/fuzz-journald.c ++++ b/src/fuzz/fuzz-journald.c +@@ -15,11 +15,15 @@ void dummy_server_init(Server *s, const uint8_t *buffer, size_t size) { + .hostname_fd = -1, + .notify_fd = -1, + .storage = STORAGE_NONE, ++ .line_max = 64, + }; + assert_se(sd_event_default(&s->event) >= 0); +- s->buffer = memdup_suffix0(buffer, size); +- assert_se(s->buffer); +- s->buffer_size = size + 1; ++ ++ if (buffer) { ++ s->buffer = memdup_suffix0(buffer, size); ++ assert_se(s->buffer); ++ s->buffer_size = size + 1; ++ } + } + + void fuzz_journald_processing_function( +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 897c02e4ae..eea9117360 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -51,6 +51,12 @@ fuzzers += [ + libshared], + [libselinux]], + ++ [['src/fuzz/fuzz-journald-stream.c', ++ 'src/fuzz/fuzz-journald.c'], ++ [libjournal_core, ++ libshared], ++ [libselinux]], ++ + [['src/fuzz/fuzz-journald-syslog.c', + 'src/fuzz/fuzz-journald.c'], + [libjournal_core, +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index dbf3503a82..6f8a4011ff 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -125,7 +125,7 @@ void stdout_stream_free(StdoutStream *s) { + + DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free); + +-static void stdout_stream_destroy(StdoutStream *s) { ++void stdout_stream_destroy(StdoutStream *s) { + if (!s) + return; + +@@ -534,7 +534,7 @@ terminate: + return 0; + } + +-static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { ++int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { + _cleanup_(stdout_stream_freep) StdoutStream *stream = NULL; + sd_id128_t id; + int r; +diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h +index bc5622ab3b..487376e763 100644 +--- a/src/journal/journald-stream.h ++++ b/src/journal/journald-stream.h +@@ -10,4 +10,6 @@ int server_open_stdout_socket(Server *s); + int server_restore_streams(Server *s, FDSet *fds); + + void stdout_stream_free(StdoutStream *s); ++int stdout_stream_install(Server *s, int fd, StdoutStream **ret); ++void stdout_stream_destroy(StdoutStream *s); + void stdout_stream_send_notify(StdoutStream *s); +diff --git a/test/fuzz/fuzz-journald-stream/basic b/test/fuzz/fuzz-journald-stream/basic +new file mode 100644 +index 0000000000..a088f1a539 +--- /dev/null ++++ b/test/fuzz/fuzz-journald-stream/basic +@@ -0,0 +1,8 @@ ++ ++ ++6 ++1 ++0 ++0 ++0 ++hey +\ No newline at end of file diff --git a/SOURCES/0263-tests-add-a-fuzzer-for-server_process_native_file.patch b/SOURCES/0263-tests-add-a-fuzzer-for-server_process_native_file.patch new file mode 100644 index 0000000..500af37 --- /dev/null +++ b/SOURCES/0263-tests-add-a-fuzzer-for-server_process_native_file.patch @@ -0,0 +1,96 @@ +From 76e2fa8ed4bbee7c625e3b790f2e38a59fffd93d Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 23 Nov 2018 00:27:19 +0100 +Subject: [PATCH] tests: add a fuzzer for server_process_native_file + +(cherry picked from commit a4aa59bae206eebb4703b291147144def5d4bb3e) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-native-fd.c | 47 ++++++++++++++++++++++++ + src/fuzz/meson.build | 6 +++ + test/fuzz/fuzz-journald-native-fd/basic | Bin 0 -> 34 bytes + 3 files changed, 53 insertions(+) + create mode 100644 src/fuzz/fuzz-journald-native-fd.c + create mode 100644 test/fuzz/fuzz-journald-native-fd/basic + +diff --git a/src/fuzz/fuzz-journald-native-fd.c b/src/fuzz/fuzz-journald-native-fd.c +new file mode 100644 +index 0000000000..95415d9f85 +--- /dev/null ++++ b/src/fuzz/fuzz-journald-native-fd.c +@@ -0,0 +1,47 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "fd-util.h" ++#include "fileio.h" ++#include "fs-util.h" ++#include "fuzz.h" ++#include "fuzz-journald.h" ++#include "journald-native.h" ++#include "memfd-util.h" ++#include "process-util.h" ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ Server s; ++ _cleanup_close_ int sealed_fd = -1, unsealed_fd = -1; ++ _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-journald-native-fd.XXXXXX"; ++ char *label = NULL; ++ size_t label_len = 0; ++ struct ucred ucred; ++ struct timeval *tv = NULL; ++ ++ if (!getenv("SYSTEMD_LOG_LEVEL")) ++ log_set_max_level(LOG_CRIT); ++ ++ dummy_server_init(&s, NULL, 0); ++ ++ sealed_fd = memfd_new(NULL); ++ assert_se(sealed_fd >= 0); ++ assert_se(write(sealed_fd, data, size) == (ssize_t) size); ++ assert_se(memfd_set_sealed(sealed_fd) >= 0); ++ assert_se(lseek(sealed_fd, 0, SEEK_SET) == 0); ++ ucred = (struct ucred) { ++ .pid = getpid_cached(), ++ .uid = geteuid(), ++ .gid = getegid(), ++ }; ++ server_process_native_file(&s, sealed_fd, &ucred, tv, label, label_len); ++ ++ unsealed_fd = mkostemp_safe(name); ++ assert_se(unsealed_fd >= 0); ++ assert_se(write(unsealed_fd, data, size) == (ssize_t) size); ++ assert_se(lseek(unsealed_fd, 0, SEEK_SET) == 0); ++ server_process_native_file(&s, unsealed_fd, &ucred, tv, label, label_len); ++ ++ server_done(&s); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index eea9117360..5315d2771c 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -51,6 +51,12 @@ fuzzers += [ + libshared], + [libselinux]], + ++ [['src/fuzz/fuzz-journald-native-fd.c', ++ 'src/fuzz/fuzz-journald.c'], ++ [libjournal_core, ++ libshared], ++ [libselinux]], ++ + [['src/fuzz/fuzz-journald-stream.c', + 'src/fuzz/fuzz-journald.c'], + [libjournal_core, +diff --git a/test/fuzz/fuzz-journald-native-fd/basic b/test/fuzz/fuzz-journald-native-fd/basic +new file mode 100644 +index 0000000000000000000000000000000000000000..65f89705a655618851c0e446efaa5c633adf425f +GIT binary patch +literal 34 +kcmeZu4Gwm6cjaPXfB;7>M@KGyCofm$koW*k7h3}^0B8*cRR910 + +literal 0 +HcmV?d00001 + diff --git a/SOURCES/0264-fuzz-journal-stream-avoid-assertion-failure-on-sampl.patch b/SOURCES/0264-fuzz-journal-stream-avoid-assertion-failure-on-sampl.patch new file mode 100644 index 0000000..798aa6c --- /dev/null +++ b/SOURCES/0264-fuzz-journal-stream-avoid-assertion-failure-on-sampl.patch @@ -0,0 +1,47 @@ +From 2d197adc6d7109d5901401a90288530582f3f991 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 26 Feb 2019 13:00:35 +0100 +Subject: [PATCH] fuzz-journal-stream: avoid assertion failure on samples which + don't fit in pipe +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11587. +We had a sample which was large enough that write(2) failed to push all the +data into the pipe, and an assert failed. The code could be changed to use +a loop, but then we'd need to interleave writes and sd_event_run (to process +the journal). I don't think the complexity is worth it — fuzzing works best +if the sample is not too huge anyway. So let's just reject samples above 64k, +and tell oss-fuzz about this limit. + +(cherry picked from commit eafadd069c4e30ed62173123326a7237448615d1) + +Resolves: #1764560 +--- + src/fuzz/fuzz-journald-stream.c | 2 +- + src/fuzz/fuzz-journald-stream.options | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + create mode 100644 src/fuzz/fuzz-journald-stream.options + +diff --git a/src/fuzz/fuzz-journald-stream.c b/src/fuzz/fuzz-journald-stream.c +index 247c0889bc..693b197d3a 100644 +--- a/src/fuzz/fuzz-journald-stream.c ++++ b/src/fuzz/fuzz-journald-stream.c +@@ -14,7 +14,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 0000000000..678d526b1e +--- /dev/null ++++ b/src/fuzz/fuzz-journald-stream.options +@@ -0,0 +1,2 @@ ++[libfuzzer] ++max_len = 65536 diff --git a/SOURCES/0265-journald-take-leading-spaces-into-account-in-syslog_.patch b/SOURCES/0265-journald-take-leading-spaces-into-account-in-syslog_.patch new file mode 100644 index 0000000..25b1d95 --- /dev/null +++ b/SOURCES/0265-journald-take-leading-spaces-into-account-in-syslog_.patch @@ -0,0 +1,45 @@ +From 3521217c88b364e2c5b10a1e35d3c036b1ecba64 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 10 Aug 2018 12:55:09 +0000 +Subject: [PATCH] journald: take leading spaces into account in + syslog_parse_identifier + +This is a kind of follow-up to e88baee88fad8bc59d3 which should finally fix +the issue which that commit was supposed to fix. + +(cherry picked from commit 937b1171378bc1000a34fcdfe9534d898227e35f) + +Resolves: #1764560 +--- + src/journal/journald-syslog.c | 3 ++- + src/journal/test-journal-syslog.c | 2 ++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c +index e0b55cc566..ae966763a0 100644 +--- a/src/journal/journald-syslog.c ++++ b/src/journal/journald-syslog.c +@@ -223,8 +223,9 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) + if (p[e] != '\0' && strchr(WHITESPACE, p[e])) + e++; + ++ l = (p - *buf) + e; + *buf = p + e; +- return e; ++ return l; + } + + static void syslog_skip_date(char **buf) { +diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c +index 120477cc9f..415b9d23ca 100644 +--- a/src/journal/test-journal-syslog.c ++++ b/src/journal/test-journal-syslog.c +@@ -41,6 +41,8 @@ int main(void) { + test_syslog_parse_identifier(" ", NULL, NULL, " ", 0); + test_syslog_parse_identifier(":", "", NULL, "", 1); + test_syslog_parse_identifier(": ", "", NULL, " ", 2); ++ test_syslog_parse_identifier(" :", "", NULL, "", 2); ++ test_syslog_parse_identifier(" pidu:", "pidu", NULL, "", 8); + test_syslog_parse_identifier("pidu:", "pidu", NULL, "", 5); + test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6); + test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0); diff --git a/SOURCES/0266-Add-a-warning-about-the-difference-in-permissions-be.patch b/SOURCES/0266-Add-a-warning-about-the-difference-in-permissions-be.patch new file mode 100644 index 0000000..8b0bd5c --- /dev/null +++ b/SOURCES/0266-Add-a-warning-about-the-difference-in-permissions-be.patch @@ -0,0 +1,43 @@ +From 5df63c2ddf93bab5e7f13e09dfb1f97a011b3451 Mon Sep 17 00:00:00 2001 +From: Taro Yamada +Date: Sun, 27 Jan 2019 13:50:04 +0900 +Subject: [PATCH] Add a warning about the difference in permissions between + existing directories and unit settings. + +To follows the intent of 30c81ce, this change does not execute chmod() and just add warnings. + +(cherry picked from commit 6cff72eb0a18d8547f005a481cd0622d3bc78483) + +Related: #1778384 +--- + src/core/execute.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 8293c522bc..9ddba00421 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2099,8 +2099,21 @@ static int setup_exec_directory( + r = mkdir_label(p, context->directories[type].mode); + if (r < 0 && r != -EEXIST) + goto fail; +- if (r == -EEXIST && !context->dynamic_user) +- continue; ++ if (r == -EEXIST) { ++ struct stat st; ++ ++ if (stat(p, &st) < 0) { ++ r = -errno; ++ 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) ++ continue; ++ } + } + + /* Don't change the owner of the configuration directory, as in the common case it is not written to by diff --git a/SOURCES/0267-execute-remove-one-redundant-comparison-check.patch b/SOURCES/0267-execute-remove-one-redundant-comparison-check.patch new file mode 100644 index 0000000..8101159 --- /dev/null +++ b/SOURCES/0267-execute-remove-one-redundant-comparison-check.patch @@ -0,0 +1,32 @@ +From 81ca39b7b38ef1d44cc146efe75bef412e7c4c97 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 14 Mar 2019 17:01:46 +0100 +Subject: [PATCH] execute: remove one redundant comparison check + +(cherry picked from commit d484580ca6f0e79abe6f3f5c677323a22d9e22d7) + +Related: #1778384 +--- + src/core/execute.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 9ddba00421..46aa733937 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2097,11 +2097,12 @@ static int setup_exec_directory( + } + } else { + r = mkdir_label(p, context->directories[type].mode); +- if (r < 0 && r != -EEXIST) +- goto fail; +- if (r == -EEXIST) { ++ if (r < 0) { + struct stat st; + ++ if (r != -EEXIST) ++ goto fail; ++ + if (stat(p, &st) < 0) { + r = -errno; + goto fail; diff --git a/SOURCES/0268-core-change-ownership-mode-of-the-execution-director.patch b/SOURCES/0268-core-change-ownership-mode-of-the-execution-director.patch new file mode 100644 index 0000000..faaef74 --- /dev/null +++ b/SOURCES/0268-core-change-ownership-mode-of-the-execution-director.patch @@ -0,0 +1,88 @@ +From 789806ac06bb13d1b579fef47dbb85f224b6dbb1 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 14 Mar 2019 17:19:30 +0100 +Subject: [PATCH] core: change ownership/mode of the execution directories also + for static users + +It's probably unexpected if we do a recursive chown() when dynamic users +are used but not on static users. + +hence, let's tweak the logic slightly, and recursively chown in both +cases, except when operating on the configuration directory. + +Fixes: #11842 +(cherry picked from commit 206e9864de460dd79d9edd7bedb47dee168765e1) + +Resolves: #1778384 +--- + src/core/execute.c | 47 +++++++++++++++++++++++++--------------------- + 1 file changed, 26 insertions(+), 21 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 46aa733937..c42300a41e 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2090,37 +2090,42 @@ 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) { +- struct stat st; +- + if (r != -EEXIST) + goto fail; + +- if (stat(p, &st) < 0) { +- r = -errno; +- 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); diff --git a/SOURCES/0269-core-dbus-execute-remove-unnecessary-initialization.patch b/SOURCES/0269-core-dbus-execute-remove-unnecessary-initialization.patch new file mode 100644 index 0000000..e46f256 --- /dev/null +++ b/SOURCES/0269-core-dbus-execute-remove-unnecessary-initialization.patch @@ -0,0 +1,25 @@ +From 5d7e8cb0e12e4642a760cf00cbb6caf4c07b9cd9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 19 May 2019 16:05:02 +0200 +Subject: [PATCH] core/dbus-execute: remove unnecessary initialization + +(cherry picked from commit bd0abfaea1514bdd7cb60228d7a3f94c17ba916d) + +Related: #1734787 +--- + src/core/dbus-execute.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 33a91c012e..5379545d57 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1552,7 +1552,7 @@ int bus_exec_context_set_transient_property( + #endif + if (streq(name, "CPUAffinity")) { + const void *a; +- size_t n = 0; ++ size_t n; + + r = sd_bus_message_read_array(message, 'y', &a, &n); + if (r < 0) diff --git a/SOURCES/0270-shared-cpu-set-util-move-the-part-to-print-cpu-set-i.patch b/SOURCES/0270-shared-cpu-set-util-move-the-part-to-print-cpu-set-i.patch new file mode 100644 index 0000000..5babb33 --- /dev/null +++ b/SOURCES/0270-shared-cpu-set-util-move-the-part-to-print-cpu-set-i.patch @@ -0,0 +1,215 @@ +From 46b4d26c54a773f7da350e89562039ccc5157a8f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 19 May 2019 18:02:38 +0200 +Subject: [PATCH] shared/cpu-set-util: move the part to print cpu-set into a + separate function + +Also avoid unnecessary asprintf() when we can write to the output area +directly. + +(cherry picked from commit a832893f9c4f0a0329768e90f67e2fa24bb0008e) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 21 +++++++++++++++++++++ + src/basic/cpu-set-util.h | 1 + + src/core/dbus-execute.c | 29 +++++------------------------ + src/test/test-cpu-set-util.c | 29 +++++++++++++++++++++++++++++ + 4 files changed, 56 insertions(+), 24 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index b1c927bcb8..8f24a2601a 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -5,6 +5,7 @@ + + #include + #include ++#include + #include + + #include "alloc-util.h" +@@ -15,6 +16,26 @@ + #include "parse-util.h" + #include "string-util.h" + ++char* cpu_set_to_string(const cpu_set_t *set, size_t setsize) { ++ _cleanup_free_ char *str = NULL; ++ size_t allocated = 0, len = 0; ++ int i, r; ++ ++ for (i = 0; (size_t) i < setsize * 8; i++) { ++ if (!CPU_ISSET_S(i, setsize, set)) ++ continue; ++ ++ if (!GREEDY_REALLOC(str, allocated, len + 1 + DECIMAL_STR_MAX(int))) ++ return NULL; ++ ++ r = sprintf(str + len, len > 0 ? " %d" : "%d", i); ++ assert_se(r > 0); ++ len += r; ++ } ++ ++ return TAKE_PTR(str) ?: strdup(""); ++} ++ + cpu_set_t* cpu_set_malloc(unsigned *ncpus) { + cpu_set_t *c; + unsigned n = 1024; +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 88470fe15a..3c546beb55 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -26,6 +26,7 @@ static inline cpu_set_t* cpu_set_mfree(cpu_set_t *p) { + + cpu_set_t* cpu_set_malloc(unsigned *ncpus); + ++char* cpu_set_to_string(const cpu_set_t *set, size_t setsize); + int parse_cpu_set_internal(const char *rvalue, cpu_set_t **cpu_set, bool warn, const char *unit, const char *filename, unsigned line, const char *lvalue); + + static inline int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue) { +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 5379545d57..d9f4445745 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1565,32 +1565,13 @@ int bus_exec_context_set_transient_property( + unit_write_settingf(u, flags, name, "%s=", name); + } else { + _cleanup_free_ char *str = NULL; +- size_t allocated = 0, len = 0, i, ncpus; ++ size_t ncpus; + +- ncpus = CPU_SIZE_TO_NUM(n); +- +- for (i = 0; i < ncpus; i++) { +- _cleanup_free_ char *p = NULL; +- size_t add; +- +- if (!CPU_ISSET_S(i, n, (cpu_set_t*) a)) +- continue; +- +- r = asprintf(&p, "%zu", i); +- if (r < 0) +- return -ENOMEM; +- +- add = strlen(p); +- +- if (!GREEDY_REALLOC(str, allocated, len + add + 2)) +- return -ENOMEM; +- +- strcpy(mempcpy(str + len, p, add), " "); +- len += add + 1; +- } ++ str = cpu_set_to_string(a, n); ++ if (!str) ++ return -ENOMEM; + +- if (len != 0) +- str[len - 1] = '\0'; ++ ncpus = CPU_SIZE_TO_NUM(n); + + if (!c->cpuset || c->cpuset_ncpus < ncpus) { + cpu_set_t *cpuset; +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index c9272459b4..ff5edb2a69 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -6,6 +6,7 @@ + + static void test_parse_cpu_set(void) { + cpu_set_t *c = NULL; ++ _cleanup_free_ char *str = NULL; + int ncpus; + int cpu; + +@@ -15,6 +16,10 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); + assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c)); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2); ++ ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* A more interesting range */ +@@ -25,6 +30,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Quoted strings */ +@@ -33,6 +41,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Use commas as separators */ +@@ -43,6 +54,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Commas with spaces (and trailing comma, space) */ +@@ -51,6 +65,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); + for (cpu = 0; cpu < 8; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Ranges */ +@@ -61,6 +78,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Ranges with trailing comma, space */ +@@ -71,6 +91,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Negative range (returns empty cpu_set) */ +@@ -85,6 +108,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12); + for (cpu = 0; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Mix ranges and individual CPUs */ +@@ -95,6 +121,9 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 4; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); + c = cpu_set_mfree(c); + + /* Garbage */ diff --git a/SOURCES/0271-shared-cpu-set-util-remove-now-unused-CPU_SIZE_TO_NU.patch b/SOURCES/0271-shared-cpu-set-util-remove-now-unused-CPU_SIZE_TO_NU.patch new file mode 100644 index 0000000..8d393c5 --- /dev/null +++ b/SOURCES/0271-shared-cpu-set-util-remove-now-unused-CPU_SIZE_TO_NU.patch @@ -0,0 +1,29 @@ +From d6935e61de30967aa82b7722f36193ba782b75e4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 19 May 2019 18:08:39 +0200 +Subject: [PATCH] shared/cpu-set-util: remove now-unused CPU_SIZE_TO_NUM() + +(cherry picked from commit b12ef7141648be40fd8c4b0209a742f2151736d9) + +Related: #1734787 +--- + src/basic/cpu-set-util.h | 6 ------ + 1 file changed, 6 deletions(-) + +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 3c546beb55..20612a8876 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -9,12 +9,6 @@ + + #include "macro.h" + +-#ifdef __NCPUBITS +-#define CPU_SIZE_TO_NUM(n) ((n) * __NCPUBITS) +-#else +-#define CPU_SIZE_TO_NUM(n) ((n) * sizeof(cpu_set_t) * 8) +-#endif +- + DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); + #define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) + diff --git a/SOURCES/0272-Rework-cpu-affinity-parsing.patch b/SOURCES/0272-Rework-cpu-affinity-parsing.patch new file mode 100644 index 0000000..f6001e5 --- /dev/null +++ b/SOURCES/0272-Rework-cpu-affinity-parsing.patch @@ -0,0 +1,932 @@ +From 61e5aed87f1b82a51c6ea8ccde96805cb63e5b15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 21 May 2019 08:45:19 +0200 +Subject: [PATCH] Rework cpu affinity parsing + +The CPU_SET_S api is pretty bad. In particular, it has a parameter for the size +of the array, but operations which take two (CPU_EQUAL_S) or even three arrays +(CPU_{AND,OR,XOR}_S) still take just one size. This means that all arrays must +be of the same size, or buffer overruns will occur. This is exactly what our +code would do, if it received an array of unexpected size over the network. +("Unexpected" here means anything different from what cpu_set_malloc() detects +as the "right" size.) + +Let's rework this, and store the size in bytes of the allocated storage area. + +The code will now parse any number up to 8191, independently of what the current +kernel supports. This matches the kernel maximum setting for any architecture, +to make things more portable. + +Fixes #12605. + +(cherry picked from commit 0985c7c4e22c8dbbea4398cf3453da45ebf63800) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 133 +++++++++++++++++++++----- + src/basic/cpu-set-util.h | 47 ++++++--- + src/core/dbus-execute.c | 35 ++----- + src/core/execute.c | 12 +-- + src/core/execute.h | 4 +- + src/core/load-fragment.c | 31 +----- + src/core/main.c | 14 +-- + src/nspawn/nspawn-settings.c | 33 +------ + src/nspawn/nspawn-settings.h | 4 +- + src/nspawn/nspawn.c | 29 +++--- + src/shared/bus-unit-util.c | 4 +- + src/test/test-cpu-set-util.c | 179 +++++++++++++++++++---------------- + src/test/test-sizeof.c | 3 + + 13 files changed, 286 insertions(+), 242 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 8f24a2601a..fe440f6381 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -15,14 +15,15 @@ + #include "macro.h" + #include "parse-util.h" + #include "string-util.h" ++#include "util.h" + +-char* cpu_set_to_string(const cpu_set_t *set, size_t setsize) { ++char* cpu_set_to_string(const CPUSet *a) { + _cleanup_free_ char *str = NULL; + size_t allocated = 0, len = 0; + int i, r; + +- for (i = 0; (size_t) i < setsize * 8; i++) { +- if (!CPU_ISSET_S(i, setsize, set)) ++ for (i = 0; (size_t) i < a->allocated * 8; i++) { ++ if (!CPU_ISSET_S(i, a->allocated, a->set)) + continue; + + if (!GREEDY_REALLOC(str, allocated, len + 1 + DECIMAL_STR_MAX(int))) +@@ -65,24 +66,74 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) { + } + } + +-int parse_cpu_set_internal( ++static int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) { ++ size_t need; ++ ++ assert(cpu_set); ++ ++ need = CPU_ALLOC_SIZE(ncpus); ++ if (need > cpu_set->allocated) { ++ cpu_set_t *t; ++ ++ t = realloc(cpu_set->set, need); ++ if (!t) ++ return -ENOMEM; ++ ++ memzero((uint8_t*) t + cpu_set->allocated, need - cpu_set->allocated); ++ ++ cpu_set->set = t; ++ cpu_set->allocated = need; ++ } ++ ++ return 0; ++} ++ ++static int cpu_set_add(CPUSet *cpu_set, unsigned cpu) { ++ int r; ++ ++ if (cpu >= 8192) ++ /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */ ++ return -ERANGE; ++ ++ r = cpu_set_realloc(cpu_set, cpu + 1); ++ if (r < 0) ++ return r; ++ ++ CPU_SET_S(cpu, cpu_set->allocated, cpu_set->set); ++ return 0; ++} ++ ++int cpu_set_add_all(CPUSet *a, const CPUSet *b) { ++ int r; ++ ++ /* Do this backwards, so if we fail, we fail before changing anything. */ ++ for (unsigned cpu_p1 = b->allocated * 8; cpu_p1 > 0; cpu_p1--) ++ if (CPU_ISSET_S(cpu_p1 - 1, b->allocated, b->set)) { ++ r = cpu_set_add(a, cpu_p1 - 1); ++ if (r < 0) ++ return r; ++ } ++ ++ return 0; ++} ++ ++int parse_cpu_set_full( + const char *rvalue, +- cpu_set_t **cpu_set, ++ CPUSet *cpu_set, + bool warn, + const char *unit, + const char *filename, + unsigned line, + const char *lvalue) { + +- _cleanup_cpu_free_ cpu_set_t *c = NULL; ++ _cleanup_(cpu_set_reset) CPUSet c = {}; + const char *p = rvalue; +- unsigned ncpus = 0; + +- assert(rvalue); ++ assert(p); + + for (;;) { + _cleanup_free_ char *word = NULL; +- unsigned cpu, cpu_lower, cpu_upper; ++ unsigned cpu_lower, cpu_upper; + int r; + + r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_QUOTES); +@@ -93,31 +144,63 @@ int parse_cpu_set_internal( + if (r == 0) + break; + +- if (!c) { +- c = cpu_set_malloc(&ncpus); +- if (!c) +- return warn ? log_oom() : -ENOMEM; +- } +- + r = parse_range(word, &cpu_lower, &cpu_upper); + if (r < 0) + return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word) : r; +- if (cpu_lower >= ncpus || cpu_upper >= ncpus) +- return warn ? log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus) : -EINVAL; + + if (cpu_lower > cpu_upper) { + if (warn) +- log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring", word, cpu_lower, cpu_upper); +- continue; ++ log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring.", ++ word, cpu_lower, cpu_upper); ++ ++ /* Make sure something is allocated, to distinguish this from the empty case */ ++ r = cpu_set_realloc(&c, 1); ++ if (r < 0) ++ return r; + } + +- for (cpu = cpu_lower; cpu <= cpu_upper; cpu++) +- CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); ++ for (unsigned cpu_p1 = MIN(cpu_upper, UINT_MAX-1) + 1; cpu_p1 > cpu_lower; cpu_p1--) { ++ r = cpu_set_add(&c, cpu_p1 - 1); ++ if (r < 0) ++ return warn ? log_syntax(unit, LOG_ERR, filename, line, r, ++ "Cannot add CPU %u to set: %m", cpu_p1 - 1) : r; ++ } + } + +- /* On success, sets *cpu_set and returns ncpus for the system. */ +- if (c) +- *cpu_set = TAKE_PTR(c); ++ /* On success, transfer ownership to the output variable */ ++ *cpu_set = c; ++ c = (CPUSet) {}; ++ ++ return 0; ++} ++ ++int parse_cpu_set_extend( ++ const char *rvalue, ++ CPUSet *old, ++ bool warn, ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *lvalue) { ++ ++ _cleanup_(cpu_set_reset) CPUSet cpuset = {}; ++ int r; ++ ++ r = parse_cpu_set_full(rvalue, &cpuset, true, unit, filename, line, lvalue); ++ if (r < 0) ++ return r; ++ ++ if (!cpuset.set) { ++ /* An empty assignment resets the CPU list */ ++ cpu_set_reset(old); ++ return 0; ++ } ++ ++ if (!old->set) { ++ *old = cpuset; ++ cpuset = (CPUSet) {}; ++ return 0; ++ } + +- return (int) ncpus; ++ return cpu_set_add_all(old, &cpuset); + } +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 20612a8876..eb31b362fe 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -12,23 +12,40 @@ + DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); + #define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) + +-static inline cpu_set_t* cpu_set_mfree(cpu_set_t *p) { +- if (p) +- CPU_FREE(p); +- return NULL; +-} +- + cpu_set_t* cpu_set_malloc(unsigned *ncpus); + +-char* cpu_set_to_string(const cpu_set_t *set, size_t setsize); +-int parse_cpu_set_internal(const char *rvalue, cpu_set_t **cpu_set, bool warn, const char *unit, const char *filename, unsigned line, const char *lvalue); +- +-static inline int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue) { +- assert(lvalue); +- +- return parse_cpu_set_internal(rvalue, cpu_set, true, unit, filename, line, lvalue); ++/* This wraps the libc interface with a variable to keep the allocated size. */ ++typedef struct CPUSet { ++ cpu_set_t *set; ++ size_t allocated; /* in bytes */ ++} CPUSet; ++ ++static inline void cpu_set_reset(CPUSet *a) { ++ assert((a->allocated > 0) == !!a->set); ++ if (a->set) ++ CPU_FREE(a->set); ++ *a = (CPUSet) {}; + } + +-static inline int parse_cpu_set(const char *rvalue, cpu_set_t **cpu_set){ +- return parse_cpu_set_internal(rvalue, cpu_set, false, NULL, NULL, 0, NULL); ++int cpu_set_add_all(CPUSet *a, const CPUSet *b); ++ ++char* cpu_set_to_string(const CPUSet *a); ++int parse_cpu_set_full( ++ const char *rvalue, ++ CPUSet *cpu_set, ++ bool warn, ++ const char *unit, ++ const char *filename, unsigned line, ++ const char *lvalue); ++int parse_cpu_set_extend( ++ const char *rvalue, ++ CPUSet *old, ++ bool warn, ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *lvalue); ++ ++static inline int parse_cpu_set(const char *rvalue, CPUSet *cpu_set){ ++ return parse_cpu_set_full(rvalue, cpu_set, false, NULL, NULL, 0, NULL); + } +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index d9f4445745..08946627e3 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -220,7 +220,7 @@ static int property_get_cpu_affinity( + assert(reply); + assert(c); + +- return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus)); ++ return sd_bus_message_append_array(reply, 'y', c->cpu_set.set, c->cpu_set.allocated); + } + + static int property_get_timer_slack_nsec( +@@ -1560,37 +1560,22 @@ int bus_exec_context_set_transient_property( + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (n == 0) { +- c->cpuset = cpu_set_mfree(c->cpuset); +- c->cpuset_ncpus = 0; ++ cpu_set_reset(&c->cpu_set); + unit_write_settingf(u, flags, name, "%s=", name); + } else { + _cleanup_free_ char *str = NULL; +- size_t ncpus; ++ const CPUSet set = {(cpu_set_t*) a, n}; + +- str = cpu_set_to_string(a, n); ++ str = cpu_set_to_string(&set); + if (!str) + return -ENOMEM; + +- ncpus = CPU_SIZE_TO_NUM(n); +- +- if (!c->cpuset || c->cpuset_ncpus < ncpus) { +- cpu_set_t *cpuset; +- +- cpuset = CPU_ALLOC(ncpus); +- if (!cpuset) +- return -ENOMEM; +- +- CPU_ZERO_S(n, cpuset); +- if (c->cpuset) { +- CPU_OR_S(CPU_ALLOC_SIZE(c->cpuset_ncpus), cpuset, c->cpuset, (cpu_set_t*) a); +- CPU_FREE(c->cpuset); +- } else +- CPU_OR_S(n, cpuset, cpuset, (cpu_set_t*) a); +- +- c->cpuset = cpuset; +- c->cpuset_ncpus = ncpus; +- } else +- CPU_OR_S(n, c->cpuset, c->cpuset, (cpu_set_t*) a); ++ /* We forego any optimizations here, and always create the structure using ++ * cpu_set_add_all(), because we don't want to care if the existing size we ++ * got over dbus is appropriate. */ ++ r = cpu_set_add_all(&c->cpu_set, &set); ++ if (r < 0) ++ return r; + + unit_write_settingf(u, flags, name, "%s=%s", name, str); + } +diff --git a/src/core/execute.c b/src/core/execute.c +index c42300a41e..22e5825905 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2991,8 +2991,8 @@ static int exec_child( + } + } + +- if (context->cpuset) +- if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { ++ if (context->cpu_set.set) ++ if (sched_setaffinity(0, context->cpu_set.allocated, context->cpu_set.set) < 0) { + *exit_status = EXIT_CPUAFFINITY; + return log_unit_error_errno(unit, errno, "Failed to set up CPU affinity: %m"); + } +@@ -3694,7 +3694,7 @@ void exec_context_done(ExecContext *c) { + c->temporary_filesystems = NULL; + c->n_temporary_filesystems = 0; + +- c->cpuset = cpu_set_mfree(c->cpuset); ++ cpu_set_reset(&c->cpu_set); + + c->utmp_id = mfree(c->utmp_id); + c->selinux_context = mfree(c->selinux_context); +@@ -4097,10 +4097,10 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { + prefix, yes_no(c->cpu_sched_reset_on_fork)); + } + +- if (c->cpuset) { ++ if (c->cpu_set.set) { + fprintf(f, "%sCPUAffinity:", prefix); +- for (i = 0; i < c->cpuset_ncpus; i++) +- if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset)) ++ for (i = 0; i < c->cpu_set.allocated * 8; i++) ++ if (CPU_ISSET_S(i, c->cpu_set.allocated, c->cpu_set.set)) + fprintf(f, " %u", i); + fputs("\n", f); + } +diff --git a/src/core/execute.h b/src/core/execute.h +index 8c91636adc..e1e7a494cd 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -14,6 +14,7 @@ typedef struct Manager Manager; + #include + + #include "cgroup-util.h" ++#include "cpu-set-util.h" + #include "fdset.h" + #include "list.h" + #include "missing.h" +@@ -148,8 +149,7 @@ struct ExecContext { + int cpu_sched_policy; + int cpu_sched_priority; + +- cpu_set_t *cpuset; +- unsigned cpuset_ncpus; ++ CPUSet cpu_set; + + ExecInput std_input; + ExecOutput std_output; +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index d9a5094aa0..34ae834188 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1211,42 +1211,13 @@ int config_parse_exec_cpu_affinity(const char *unit, + void *userdata) { + + ExecContext *c = data; +- _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; +- int ncpus; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + +- ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue); +- if (ncpus < 0) +- return ncpus; +- +- if (ncpus == 0) { +- /* An empty assignment resets the CPU list */ +- c->cpuset = cpu_set_mfree(c->cpuset); +- c->cpuset_ncpus = 0; +- return 0; +- } +- +- if (!c->cpuset) { +- c->cpuset = TAKE_PTR(cpuset); +- c->cpuset_ncpus = (unsigned) ncpus; +- return 0; +- } +- +- if (c->cpuset_ncpus < (unsigned) ncpus) { +- CPU_OR_S(CPU_ALLOC_SIZE(c->cpuset_ncpus), cpuset, c->cpuset, cpuset); +- CPU_FREE(c->cpuset); +- c->cpuset = TAKE_PTR(cpuset); +- c->cpuset_ncpus = (unsigned) ncpus; +- return 0; +- } +- +- CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), c->cpuset, c->cpuset, cpuset); +- +- return 0; ++ return parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue); + } + + int config_parse_capability_set( +diff --git a/src/core/main.c b/src/core/main.c +index af7b26d6f1..e62b2756ee 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -537,16 +537,18 @@ static int config_parse_cpu_affinity2( + void *data, + void *userdata) { + +- _cleanup_cpu_free_ cpu_set_t *c = NULL; +- int ncpus; ++ _cleanup_(cpu_set_reset) CPUSet c = {}; ++ int r; + +- ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue); +- if (ncpus < 0) +- return ncpus; ++ r = parse_cpu_set_full(rvalue, &c, true, unit, filename, line, lvalue); ++ if (r < 0) ++ return r; + +- if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) ++ if (sched_setaffinity(0, c.allocated, c.set) < 0) + log_warning_errno(errno, "Failed to set CPU affinity: %m"); + ++ // FIXME: parsing and execution should be seperated. ++ + return 0; + } + +diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c +index 62a3486952..21c24a1111 100644 +--- a/src/nspawn/nspawn-settings.c ++++ b/src/nspawn/nspawn-settings.c +@@ -85,7 +85,7 @@ Settings* settings_free(Settings *s) { + strv_free(s->syscall_blacklist); + rlimit_free_all(s->rlimit); + free(s->hostname); +- s->cpuset = cpu_set_mfree(s->cpuset); ++ cpu_set_reset(&s->cpu_set); + + strv_free(s->network_interfaces); + strv_free(s->network_macvlan); +@@ -687,41 +687,12 @@ int config_parse_cpu_affinity( + void *data, + void *userdata) { + +- _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; + Settings *settings = data; +- int ncpus; + + assert(rvalue); + assert(settings); + +- ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue); +- if (ncpus < 0) +- return ncpus; +- +- if (ncpus == 0) { +- /* An empty assignment resets the CPU list */ +- settings->cpuset = cpu_set_mfree(settings->cpuset); +- settings->cpuset_ncpus = 0; +- return 0; +- } +- +- if (!settings->cpuset) { +- settings->cpuset = TAKE_PTR(cpuset); +- settings->cpuset_ncpus = (unsigned) ncpus; +- return 0; +- } +- +- if (settings->cpuset_ncpus < (unsigned) ncpus) { +- CPU_OR_S(CPU_ALLOC_SIZE(settings->cpuset_ncpus), cpuset, settings->cpuset, cpuset); +- CPU_FREE(settings->cpuset); +- settings->cpuset = TAKE_PTR(cpuset); +- settings->cpuset_ncpus = (unsigned) ncpus; +- return 0; +- } +- +- CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset); +- +- return 0; ++ return parse_cpu_set_extend(rvalue, &settings->cpu_set, true, unit, filename, line, lvalue); + } + + DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode"); +diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h +index d522f3cb36..da863ef11c 100644 +--- a/src/nspawn/nspawn-settings.h ++++ b/src/nspawn/nspawn-settings.h +@@ -7,6 +7,7 @@ + #include "sd-id128.h" + + #include "conf-parser.h" ++#include "cpu-set-util.h" + #include "macro.h" + #include "nspawn-expose-ports.h" + #include "nspawn-mount.h" +@@ -123,8 +124,7 @@ typedef struct Settings { + int no_new_privileges; + int oom_score_adjust; + bool oom_score_adjust_set; +- cpu_set_t *cpuset; +- unsigned cpuset_ncpus; ++ CPUSet cpu_set; + ResolvConfMode resolv_conf; + LinkJournal link_journal; + bool link_journal_try; +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index b40411dcd0..08255b5724 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -199,8 +199,7 @@ static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {}; + static bool arg_no_new_privileges = false; + static int arg_oom_score_adjust = 0; + static bool arg_oom_score_adjust_set = false; +-static cpu_set_t *arg_cpuset = NULL; +-static unsigned arg_cpuset_ncpus = 0; ++static CPUSet arg_cpu_set = {}; + static ResolvConfMode arg_resolv_conf = RESOLV_CONF_AUTO; + static TimezoneMode arg_timezone = TIMEZONE_AUTO; + +@@ -1186,17 +1185,14 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_CPU_AFFINITY: { +- _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; ++ CPUSet cpuset; + + r = parse_cpu_set(optarg, &cpuset); + if (r < 0) +- return log_error_errno(r, "Failed to parse CPU affinity mask: %s", optarg); ++ return log_error_errno(r, "Failed to parse CPU affinity mask %s: %m", optarg); + +- if (arg_cpuset) +- CPU_FREE(arg_cpuset); +- +- arg_cpuset = TAKE_PTR(cpuset); +- arg_cpuset_ncpus = r; ++ cpu_set_reset(&arg_cpu_set); ++ arg_cpu_set = cpuset; + arg_settings_mask |= SETTING_CPU_AFFINITY; + break; + } +@@ -2631,8 +2627,8 @@ static int inner_child( + return log_error_errno(r, "Failed to adjust OOM score: %m"); + } + +- if (arg_cpuset) +- if (sched_setaffinity(0, CPU_ALLOC_SIZE(arg_cpuset_ncpus), arg_cpuset) < 0) ++ if (arg_cpu_set.set) ++ if (sched_setaffinity(0, arg_cpu_set.allocated, arg_cpu_set.set) < 0) + return log_error_errno(errno, "Failed to set CPU affinity: %m"); + + r = drop_capabilities(); +@@ -3494,15 +3490,14 @@ static int merge_settings(Settings *settings, const char *path) { + } + + if ((arg_settings_mask & SETTING_CPU_AFFINITY) == 0 && +- settings->cpuset) { ++ settings->cpu_set.set) { + + if (!arg_settings_trusted) + log_warning("Ignoring CPUAffinity= setting, file '%s' is not trusted.", path); + else { +- if (arg_cpuset) +- CPU_FREE(arg_cpuset); +- arg_cpuset = TAKE_PTR(settings->cpuset); +- arg_cpuset_ncpus = settings->cpuset_ncpus; ++ cpu_set_reset(&arg_cpu_set); ++ arg_cpu_set = settings->cpu_set; ++ settings->cpu_set = (CPUSet) {}; + } + } + +@@ -4600,7 +4595,7 @@ finish: + rlimit_free_all(arg_rlimit); + strv_free(arg_syscall_whitelist); + strv_free(arg_syscall_blacklist); +- arg_cpuset = cpu_set_mfree(arg_cpuset); ++ cpu_set_reset(&arg_cpu_set); + + return r < 0 ? EXIT_FAILURE : ret; + } +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 271cc054da..75b4aace84 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -932,13 +932,13 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + } + + if (streq(field, "CPUAffinity")) { +- _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; ++ _cleanup_(cpu_set_reset) CPUSet cpuset = {}; + + r = parse_cpu_set(eq, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + +- return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r)); ++ return bus_append_byte_array(m, field, cpuset.set, cpuset.allocated); + } + + if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) { +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index ff5edb2a69..b9ec29af66 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -1,154 +1,171 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + ++#include ++ + #include "alloc-util.h" + #include "cpu-set-util.h" + #include "macro.h" + + static void test_parse_cpu_set(void) { +- cpu_set_t *c = NULL; ++ CPUSet c = {}; + _cleanup_free_ char *str = NULL; +- int ncpus; + int cpu; + + /* Simple range (from CPUAffinity example) */ +- ncpus = parse_cpu_set_and_warn("1 2", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2); +- +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(parse_cpu_set_full("1 2", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.set); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_ISSET_S(1, c.allocated, c.set)); ++ assert_se(CPU_ISSET_S(2, c.allocated, c.set)); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 2); ++ ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* A more interesting range */ +- ncpus = parse_cpu_set_and_warn("0 1 2 3 8 9 10 11", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); ++ assert_se(parse_cpu_set_full("0 1 2 3 8 9 10 11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); + for (cpu = 8; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Quoted strings */ +- ncpus = parse_cpu_set_and_warn("8 '9' 10 \"11\"", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4); ++ assert_se(parse_cpu_set_full("8 '9' 10 \"11\"", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 4); + for (cpu = 8; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Use commas as separators */ +- ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,11", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); ++ assert_se(parse_cpu_set_full("0,1,2,3 8,9,10,11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); + for (cpu = 8; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Commas with spaces (and trailing comma, space) */ +- ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); ++ assert_se(parse_cpu_set_full("0, 1, 2, 3, 4, 5, 6, 7, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 8; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Ranges */ +- ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); ++ assert_se(parse_cpu_set_full("0-3,8-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); + for (cpu = 8; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Ranges with trailing comma, space */ +- ncpus = parse_cpu_set_and_warn("0-3 8-11, ", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); ++ assert_se(parse_cpu_set_full("0-3 8-11, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); + for (cpu = 8; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Negative range (returns empty cpu_set) */ +- ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0); +- c = cpu_set_mfree(c); ++ assert_se(parse_cpu_set_full("3-0", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 0); ++ cpu_set_reset(&c); + + /* Overlapping ranges */ +- ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12); ++ assert_se(parse_cpu_set_full("0-7 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 12); + for (cpu = 0; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Mix ranges and individual CPUs */ +- ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus >= 1024); +- assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10); +- assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); ++ assert_se(parse_cpu_set_full("0,1 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 10); ++ assert_se(CPU_ISSET_S(0, c.allocated, c.set)); ++ assert_se(CPU_ISSET_S(1, c.allocated, c.set)); + for (cpu = 4; cpu < 12; cpu++) +- assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); +- assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus))); ++ assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); +- c = cpu_set_mfree(c); ++ cpu_set_reset(&c); + + /* Garbage */ +- ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus < 0); +- assert_se(!c); ++ assert_se(parse_cpu_set_full("0 1 2 3 garbage", &c, true, NULL, "fake", 1, "CPUAffinity") == -EINVAL); ++ assert_se(!c.set); ++ assert_se(c.allocated == 0); + + /* Range with garbage */ +- ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus < 0); +- assert_se(!c); ++ assert_se(parse_cpu_set_full("0-3 8-garbage", &c, true, NULL, "fake", 1, "CPUAffinity") == -EINVAL); ++ assert_se(!c.set); ++ assert_se(c.allocated == 0); + + /* Empty string */ +- c = NULL; +- ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus == 0); /* empty string returns 0 */ +- assert_se(!c); ++ assert_se(parse_cpu_set_full("", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(!c.set); /* empty string returns NULL */ ++ assert_se(c.allocated == 0); + + /* Runaway quoted string */ +- ncpus = parse_cpu_set_and_warn("0 1 2 3 \"4 5 6 7 ", &c, NULL, "fake", 1, "CPUAffinity"); +- assert_se(ncpus < 0); +- assert_se(!c); ++ assert_se(parse_cpu_set_full("0 1 2 3 \"4 5 6 7 ", &c, true, NULL, "fake", 1, "CPUAffinity") == -EINVAL); ++ assert_se(!c.set); ++ assert_se(c.allocated == 0); ++ ++ /* Maximum allocation */ ++ assert_se(parse_cpu_set_full("8000-8191", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 192); ++ assert_se(str = cpu_set_to_string(&c)); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); ++ cpu_set_reset(&c); + } + + int main(int argc, char *argv[]) { ++ log_info("CPU_ALLOC_SIZE(1) = %zu", CPU_ALLOC_SIZE(1)); ++ log_info("CPU_ALLOC_SIZE(9) = %zu", CPU_ALLOC_SIZE(9)); ++ log_info("CPU_ALLOC_SIZE(64) = %zu", CPU_ALLOC_SIZE(64)); ++ log_info("CPU_ALLOC_SIZE(65) = %zu", CPU_ALLOC_SIZE(65)); ++ log_info("CPU_ALLOC_SIZE(1024) = %zu", CPU_ALLOC_SIZE(1024)); ++ log_info("CPU_ALLOC_SIZE(1025) = %zu", CPU_ALLOC_SIZE(1025)); ++ log_info("CPU_ALLOC_SIZE(8191) = %zu", CPU_ALLOC_SIZE(8191)); ++ + test_parse_cpu_set(); + + return 0; +diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c +index 7a1e496ed2..396e68f35f 100644 +--- a/src/test/test-sizeof.c ++++ b/src/test/test-sizeof.c +@@ -1,5 +1,6 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + ++#include + #include + #include + +@@ -64,6 +65,8 @@ int main(void) { + info(uid_t); + info(gid_t); + ++ info(__cpu_mask); ++ + info(enum Enum); + info(enum BigEnum); + info(enum BigEnum2); diff --git a/SOURCES/0273-Move-cpus_in_affinity_mask-to-cpu-set-util.-ch.patch b/SOURCES/0273-Move-cpus_in_affinity_mask-to-cpu-set-util.-ch.patch new file mode 100644 index 0000000..43f5d04 --- /dev/null +++ b/SOURCES/0273-Move-cpus_in_affinity_mask-to-cpu-set-util.-ch.patch @@ -0,0 +1,125 @@ +From 42032749e61076b3d9e5004432073c2a5ea737ce Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 28 May 2019 21:28:31 +0200 +Subject: [PATCH] Move cpus_in_affinity_mask() to cpu-set-util.[ch] + +It just seems to fit better there and it's always better to have things +in shared/ rather than basic/. + +(cherry picked from commit f44b3035d4a698aa0ce08a552199b54d43de3d85) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 34 ++++++++++++++++++++++++++++++++++ + src/basic/cpu-set-util.h | 2 ++ + src/basic/process-util.c | 5 ++--- + src/shared/condition.c | 1 + + src/test/test-condition.c | 1 + + 5 files changed, 40 insertions(+), 3 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index fe440f6381..1803539ac6 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -204,3 +204,37 @@ int parse_cpu_set_extend( + + return cpu_set_add_all(old, &cpuset); + } ++ ++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; ++ } ++} +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index eb31b362fe..9b026aca09 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -49,3 +49,5 @@ int parse_cpu_set_extend( + static inline int parse_cpu_set(const char *rvalue, CPUSet *cpu_set){ + return parse_cpu_set_full(rvalue, cpu_set, false, NULL, NULL, 0, NULL); + } ++ ++int cpus_in_affinity_mask(void); +diff --git a/src/basic/process-util.c b/src/basic/process-util.c +index 6dbeee9dda..0a4a747ba4 100644 +--- a/src/basic/process-util.c ++++ b/src/basic/process-util.c +@@ -4,7 +4,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -1474,7 +1473,7 @@ static const char *const ioprio_class_table[] = { + [IOPRIO_CLASS_NONE] = "none", + [IOPRIO_CLASS_RT] = "realtime", + [IOPRIO_CLASS_BE] = "best-effort", +- [IOPRIO_CLASS_IDLE] = "idle" ++ [IOPRIO_CLASS_IDLE] = "idle", + }; + + DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES); +@@ -1495,7 +1494,7 @@ static const char* const sched_policy_table[] = { + [SCHED_BATCH] = "batch", + [SCHED_IDLE] = "idle", + [SCHED_FIFO] = "fifo", +- [SCHED_RR] = "rr" ++ [SCHED_RR] = "rr", + }; + + DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX); +diff --git a/src/shared/condition.c b/src/shared/condition.c +index 2969a89b4e..b829f0528c 100644 +--- a/src/shared/condition.c ++++ b/src/shared/condition.c +@@ -21,6 +21,7 @@ + #include "cap-list.h" + #include "cgroup-util.h" + #include "condition.h" ++#include "cpu-set-util.h" + #include "efivars.h" + #include "extract-word.h" + #include "fd-util.h" +diff --git a/src/test/test-condition.c b/src/test/test-condition.c +index 7ce6ee80ea..24395dafc6 100644 +--- a/src/test/test-condition.c ++++ b/src/test/test-condition.c +@@ -13,6 +13,7 @@ + #include "audit-util.h" + #include "cgroup-util.h" + #include "condition.h" ++#include "cpu-set-util.h" + #include "efivars.h" + #include "hostname-util.h" + #include "id128-util.h" diff --git a/SOURCES/0274-test-cpu-set-util-add-simple-test-for-cpus_in_affini.patch b/SOURCES/0274-test-cpu-set-util-add-simple-test-for-cpus_in_affini.patch new file mode 100644 index 0000000..026cee1 --- /dev/null +++ b/SOURCES/0274-test-cpu-set-util-add-simple-test-for-cpus_in_affini.patch @@ -0,0 +1,40 @@ +From a1ed6bfc5a8c40377b9f1cab1acc3c67a9529427 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 21 May 2019 09:01:34 +0200 +Subject: [PATCH] test-cpu-set-util: add simple test for + cpus_in_affinity_mask() + +(cherry picked from commit 9d1345f0657c707df89b41b2738776efb40aec8e) + +Related: #1734787 +--- + src/test/test-cpu-set-util.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index b9ec29af66..e87e0ca6e6 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -157,6 +157,14 @@ static void test_parse_cpu_set(void) { + cpu_set_reset(&c); + } + ++static void test_cpus_in_affinity_mask(void) { ++ int r; ++ ++ r = cpus_in_affinity_mask(); ++ assert(r > 0); ++ log_info("cpus_in_affinity_mask: %d", r); ++} ++ + int main(int argc, char *argv[]) { + log_info("CPU_ALLOC_SIZE(1) = %zu", CPU_ALLOC_SIZE(1)); + log_info("CPU_ALLOC_SIZE(9) = %zu", CPU_ALLOC_SIZE(9)); +@@ -167,6 +175,7 @@ int main(int argc, char *argv[]) { + log_info("CPU_ALLOC_SIZE(8191) = %zu", CPU_ALLOC_SIZE(8191)); + + test_parse_cpu_set(); ++ test_cpus_in_affinity_mask(); + + return 0; + } diff --git a/SOURCES/0275-test-cpu-set-util-add-a-smoke-test-for-test_parse_cp.patch b/SOURCES/0275-test-cpu-set-util-add-a-smoke-test-for-test_parse_cp.patch new file mode 100644 index 0000000..cce5c67 --- /dev/null +++ b/SOURCES/0275-test-cpu-set-util-add-a-smoke-test-for-test_parse_cp.patch @@ -0,0 +1,63 @@ +From 69541e93c45efb7ee15d7584c3aa70c3ff0b2200 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 24 May 2019 08:50:41 +0200 +Subject: [PATCH] test-cpu-set-util: add a smoke test for + test_parse_cpu_set_extend() + +(cherry picked from commit b54d7241f25b859c6c008e516c2131c39902e6e4) + +Related: #1734787 +--- + src/test/test-cpu-set-util.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index e87e0ca6e6..81f67647e8 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -11,6 +11,8 @@ static void test_parse_cpu_set(void) { + _cleanup_free_ char *str = NULL; + int cpu; + ++ log_info("/* %s */", __func__); ++ + /* Simple range (from CPUAffinity example) */ + assert_se(parse_cpu_set_full("1 2", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.set); +@@ -157,6 +159,28 @@ static void test_parse_cpu_set(void) { + cpu_set_reset(&c); + } + ++static void test_parse_cpu_set_extend(void) { ++ CPUSet c = {}; ++ _cleanup_free_ char *s1 = NULL, *s2 = NULL; ++ ++ log_info("/* %s */", __func__); ++ ++ assert_se(parse_cpu_set_extend("1 3", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 2); ++ assert_se(s1 = cpu_set_to_string(&c)); ++ log_info("cpu_set_to_string: %s", s1); ++ ++ assert_se(parse_cpu_set_extend("4", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 3); ++ assert_se(s2 = cpu_set_to_string(&c)); ++ log_info("cpu_set_to_string: %s", s2); ++ ++ assert_se(parse_cpu_set_extend("", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(!c.set); ++ assert_se(c.allocated == 0); ++ log_info("cpu_set_to_string: (null)"); ++} ++ + static void test_cpus_in_affinity_mask(void) { + int r; + +@@ -175,6 +199,7 @@ int main(int argc, char *argv[]) { + log_info("CPU_ALLOC_SIZE(8191) = %zu", CPU_ALLOC_SIZE(8191)); + + test_parse_cpu_set(); ++ test_parse_cpu_set_extend(); + test_cpus_in_affinity_mask(); + + return 0; diff --git a/SOURCES/0276-pid1-parse-CPUAffinity-in-incremental-fashion.patch b/SOURCES/0276-pid1-parse-CPUAffinity-in-incremental-fashion.patch new file mode 100644 index 0000000..948c139 --- /dev/null +++ b/SOURCES/0276-pid1-parse-CPUAffinity-in-incremental-fashion.patch @@ -0,0 +1,148 @@ +From 8bf8409c6e08f5aef35d1976e172b3f61b651c8d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 24 May 2019 08:35:51 +0200 +Subject: [PATCH] pid1: parse CPUAffinity= in incremental fashion + +This makes the handling of this option match what we do in unit files. I think +consistency is important here. (As it happens, it is the only option in +system.conf that is "non-atomic", i.e. where there's a list of things which can +be split over multiple assignments. All other options are single-valued, so +there's no issue of how to handle multiple assignments.) + +(cherry picked from commit 61fbbac1d517a0b3498a689c736c6ca918497904) + +Related: #1734787 +--- + man/systemd-system.conf.xml | 13 ++++++++----- + man/systemd.exec.xml | 2 +- + src/core/main.c | 36 ++++++++++++++++++++++++++---------- + 3 files changed, 35 insertions(+), 16 deletions(-) + +diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml +index 085086200a..ab23779ec0 100644 +--- a/man/systemd-system.conf.xml ++++ b/man/systemd-system.conf.xml +@@ -99,11 +99,14 @@ + + CPUAffinity= + +- Configures the initial CPU affinity for the +- init process. Takes a list of CPU indices or ranges separated +- by either whitespace or commas. CPU ranges are specified by +- the lower and upper CPU indices separated by a +- dash. ++ Configures the CPU affinity for the service manager as well as the default CPU ++ affinity for all forked off processes. Takes a list of CPU indices or ranges separated by either ++ whitespace or commas. CPU ranges are specified by the lower and upper CPU indices separated by a ++ dash. This option may be specified more than once, in which case the specified CPU affinity masks are ++ merged. If the empty string is assigned, the mask is reset, all assignments prior to this will have ++ no effect. Individual services may override the CPU affinity for their processes with the ++ CPUAffinity= setting in unit files, see ++ systemd.exec5. + + + +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index 737c52bcc4..342b8385bc 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -703,7 +703,7 @@ CapabilityBoundingSet=~CAP_B CAP_C + + Controls the CPU affinity of the executed processes. Takes a list of CPU indices or ranges + separated by either whitespace or commas. CPU ranges are specified by the lower and upper CPU indices separated +- by a dash. This option may be specified more than once, in which case the specified CPU affinity masks are ++ by a dash. This option may be specified more than once, in which case the specified CPU affinity masks are + merged. If the empty string is assigned, the mask is reset, all assignments prior to this will have no + effect. See + sched_setaffinity2 for +diff --git a/src/core/main.c b/src/core/main.c +index e62b2756ee..bc1db2af7b 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -127,6 +127,7 @@ static bool arg_default_tasks_accounting = true; + static uint64_t arg_default_tasks_max = UINT64_MAX; + static sd_id128_t arg_machine_id = {}; + static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; ++static CPUSet arg_cpu_affinity = {}; + + _noreturn_ static void freeze_or_reboot(void) { + +@@ -537,17 +538,11 @@ static int config_parse_cpu_affinity2( + void *data, + void *userdata) { + +- _cleanup_(cpu_set_reset) CPUSet c = {}; +- int r; +- +- r = parse_cpu_set_full(rvalue, &c, true, unit, filename, line, lvalue); +- if (r < 0) +- return r; ++ CPUSet *affinity = data; + +- if (sched_setaffinity(0, c.allocated, c.set) < 0) +- log_warning_errno(errno, "Failed to set CPU affinity: %m"); ++ assert(affinity); + +- // FIXME: parsing and execution should be seperated. ++ (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue); + + return 0; + } +@@ -655,7 +650,7 @@ static int parse_config_file(void) { + { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell }, + { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot }, + { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status }, +- { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL }, ++ { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity }, + { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, + { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog }, + { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog }, +@@ -1483,6 +1478,21 @@ static void initialize_coredump(bool skip_setup) { + #endif + } + ++static void update_cpu_affinity(bool skip_setup) { ++ _cleanup_free_ char *mask = NULL; ++ ++ if (skip_setup || !arg_cpu_affinity.set) ++ return; ++ ++ assert(arg_cpu_affinity.allocated > 0); ++ ++ mask = cpu_set_to_string(&arg_cpu_affinity); ++ log_debug("Setting CPU affinity to %s.", strnull(mask)); ++ ++ if (sched_setaffinity(0, arg_cpu_affinity.allocated, arg_cpu_affinity.set) < 0) ++ log_warning_errno(errno, "Failed to set CPU affinity: %m"); ++} ++ + static void do_reexecute( + int argc, + char *argv[], +@@ -1655,6 +1665,8 @@ static int invoke_main_loop( + + set_manager_defaults(m); + ++ update_cpu_affinity(false); ++ + if (saved_log_level >= 0) + manager_override_log_level(m, saved_log_level); + if (saved_log_target >= 0) +@@ -1813,6 +1825,8 @@ static int initialize_runtime( + if (arg_action != ACTION_RUN) + return 0; + ++ update_cpu_affinity(skip_setup); ++ + if (arg_system) { + /* Make sure we leave a core dump without panicing the kernel. */ + install_crash_handler(); +@@ -1947,6 +1961,8 @@ static void free_arguments(void) { + arg_join_controllers = strv_free_free(arg_join_controllers); + arg_default_environment = strv_free(arg_default_environment); + arg_syscall_archs = set_free(arg_syscall_archs); ++ ++ cpu_set_reset(&arg_cpu_affinity); + } + + static int load_configuration(int argc, char **argv, const char **ret_error_message) { diff --git a/SOURCES/0277-pid1-don-t-reset-setting-from-proc-cmdline-upon-rest.patch b/SOURCES/0277-pid1-don-t-reset-setting-from-proc-cmdline-upon-rest.patch new file mode 100644 index 0000000..bb8d822 --- /dev/null +++ b/SOURCES/0277-pid1-don-t-reset-setting-from-proc-cmdline-upon-rest.patch @@ -0,0 +1,86 @@ +From f71f3271fa149d2b5f022830d43071d97b022b38 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 24 May 2019 08:59:23 +0200 +Subject: [PATCH] pid1: don't reset setting from /proc/cmdline upon restart + +We have settings which may be set on the kernel command line, and also +in /proc/cmdline (for pid1). The settings in /proc/cmdline have higher priority +of course. When a reload was done, we'd reload just the configuration file, +losing the overrides. + +So read /proc/cmdline again during reload. + +Also, when initially reading the configuration file when program starts, +don't treat any errors as fatal. The configuration done in there doesn't +seem important enough to refuse boot. + +(cherry picked from commit 470a5e6dcec4637439ae953002127af214d396ac) + +Related: #1734787 +--- + src/core/main.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index bc1db2af7b..9a9f145080 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -129,6 +129,8 @@ static sd_id128_t arg_machine_id = {}; + static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; + static CPUSet arg_cpu_affinity = {}; + ++static int parse_configuration(void); ++ + _noreturn_ static void freeze_or_reboot(void) { + + if (arg_crash_reboot) { +@@ -1659,9 +1661,7 @@ static int invoke_main_loop( + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + +- r = parse_config_file(); +- if (r < 0) +- log_warning_errno(r, "Failed to parse config file, ignoring: %m"); ++ (void) parse_configuration(); + + set_manager_defaults(m); + +@@ -1965,18 +1965,14 @@ static void free_arguments(void) { + cpu_set_reset(&arg_cpu_affinity); + } + +-static int load_configuration(int argc, char **argv, const char **ret_error_message) { ++static int parse_configuration(void) { + int r; + +- assert(ret_error_message); +- + arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); + + r = parse_config_file(); +- if (r < 0) { +- *ret_error_message = "Failed to parse config file"; +- return r; +- } ++ if (r < 0) ++ log_warning_errno(r, "Failed to parse config file, ignoring: %m"); + + if (arg_system) { + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); +@@ -1987,6 +1983,16 @@ static int load_configuration(int argc, char **argv, const char **ret_error_mess + /* Note that this also parses bits from the kernel command line, including "debug". */ + log_parse_environment(); + ++ return 0; ++} ++ ++static int load_configuration(int argc, char **argv, const char **ret_error_message) { ++ int r; ++ ++ assert(ret_error_message); ++ ++ (void) parse_configuration(); ++ + r = parse_argv(argc, argv); + if (r < 0) { + *ret_error_message = "Failed to parse commandline arguments"; diff --git a/SOURCES/0278-pid1-when-reloading-configuration-forget-old-setting.patch b/SOURCES/0278-pid1-when-reloading-configuration-forget-old-setting.patch new file mode 100644 index 0000000..1b941cc --- /dev/null +++ b/SOURCES/0278-pid1-when-reloading-configuration-forget-old-setting.patch @@ -0,0 +1,208 @@ +From 0387294ba41ceaf80c79621409aab9508732bda0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 24 May 2019 09:41:44 +0200 +Subject: [PATCH] pid1: when reloading configuration, forget old settings + +If we had a configuration setting from a configuration file, and it was +removed, we'd still remember the old value, because there's was no mechanism to +"reset" everything, just to assign new values. + +Note that the effect of this is limited. For settings that have an "ongoing" effect, +like systemd.confirm_spawn, the new value is simply used. But some settings can only +be set at start. + +In particular, CPUAffinity= will be updated if set to a new value, but if +CPUAffinity= is fully removed, it will not be reset, simply because we don't +know what to reset it to. We might have inherited a setting, or we might have +set it ourselves. In principle we could remember the "original" value that was +set when we were executed, but propagate this over reloads and reexecs, but +that would be a lot of work for little gain. So this corner case of removal of +CPUAffinity= is not handled fully, and a reboot is needed to execute the +change. As a work-around, a full mask of CPUAffinity=0-8191 can be specified. + +(cherry picked from commit fb39af4ce42d7ef9af63009f271f404038703704) + +Related: #1734787 +--- + src/core/main.c | 139 +++++++++++++++++++++++++++++++++--------------- + 1 file changed, 95 insertions(+), 44 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index 9a9f145080..c74dc641c1 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -88,46 +88,52 @@ static enum { + ACTION_DUMP_CONFIGURATION_ITEMS, + ACTION_DUMP_BUS_PROPERTIES, + } arg_action = ACTION_RUN; +-static char *arg_default_unit = NULL; +-static bool arg_system = false; +-static bool arg_dump_core = true; +-static int arg_crash_chvt = -1; +-static bool arg_crash_shell = false; +-static bool arg_crash_reboot = false; +-static char *arg_confirm_spawn = NULL; +-static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; +-static bool arg_switched_root = false; +-static bool arg_no_pager = false; +-static bool arg_service_watchdogs = true; ++ ++/* Those variables are initalized to 0 automatically, so we avoid uninitialized memory access. ++ * Real defaults are assigned in reset_arguments() below. */ ++static char *arg_default_unit; ++static bool arg_system; ++static bool arg_dump_core; ++static int arg_crash_chvt; ++static bool arg_crash_shell; ++static bool arg_crash_reboot; ++static char *arg_confirm_spawn; ++static ShowStatus arg_show_status; ++static bool arg_switched_root; ++static bool arg_no_pager; ++static bool arg_service_watchdogs; + static char ***arg_join_controllers = NULL; +-static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; +-static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; +-static usec_t arg_default_restart_usec = DEFAULT_RESTART_USEC; +-static usec_t arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC; +-static usec_t arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC; +-static usec_t arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL; +-static unsigned arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST; +-static usec_t arg_runtime_watchdog = 0; +-static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE; +-static char *arg_watchdog_device = NULL; +-static char **arg_default_environment = NULL; +-static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {}; +-static uint64_t arg_capability_bounding_set = CAP_ALL; +-static bool arg_no_new_privs = false; +-static nsec_t arg_timer_slack_nsec = NSEC_INFINITY; +-static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE; +-static Set* arg_syscall_archs = NULL; +-static FILE* arg_serialization = NULL; +-static bool arg_default_cpu_accounting = false; +-static bool arg_default_io_accounting = false; +-static bool arg_default_ip_accounting = false; +-static bool arg_default_blockio_accounting = false; +-static bool arg_default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT; +-static bool arg_default_tasks_accounting = true; +-static uint64_t arg_default_tasks_max = UINT64_MAX; +-static sd_id128_t arg_machine_id = {}; +-static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; +-static CPUSet arg_cpu_affinity = {}; ++static ExecOutput arg_default_std_output; ++static ExecOutput arg_default_std_error; ++static usec_t arg_default_restart_usec; ++static usec_t arg_default_timeout_start_usec; ++static usec_t arg_default_timeout_stop_usec; ++static usec_t arg_default_timeout_abort_usec; ++static bool arg_default_timeout_abort_set; ++static usec_t arg_default_start_limit_interval; ++static unsigned arg_default_start_limit_burst; ++static usec_t arg_runtime_watchdog; ++static usec_t arg_shutdown_watchdog; ++static char *arg_early_core_pattern; ++static char *arg_watchdog_device; ++static char **arg_default_environment; ++static struct rlimit *arg_default_rlimit[_RLIMIT_MAX]; ++static uint64_t arg_capability_bounding_set; ++static bool arg_no_new_privs; ++static nsec_t arg_timer_slack_nsec; ++static usec_t arg_default_timer_accuracy_usec; ++static Set* arg_syscall_archs; ++static FILE* arg_serialization; ++static int arg_default_cpu_accounting; ++static bool arg_default_io_accounting; ++static bool arg_default_ip_accounting; ++static bool arg_default_blockio_accounting; ++static bool arg_default_memory_accounting; ++static bool arg_default_tasks_accounting; ++static uint64_t arg_default_tasks_max; ++static sd_id128_t arg_machine_id; ++static EmergencyAction arg_cad_burst_action; ++static CPUSet arg_cpu_affinity; + + static int parse_configuration(void); + +@@ -1951,17 +1957,59 @@ static int do_queue_default_job( + return 0; + } + +-static void free_arguments(void) { +- +- /* Frees all arg_* variables, with the exception of arg_serialization */ +- rlimit_free_all(arg_default_rlimit); ++static void reset_arguments(void) { ++ /* Frees/resets arg_* variables, with a few exceptions commented below. */ + + arg_default_unit = mfree(arg_default_unit); ++ ++ /* arg_system — ignore */ ++ ++ arg_dump_core = true; ++ arg_crash_chvt = -1; ++ arg_crash_shell = false; ++ arg_crash_reboot = false; + arg_confirm_spawn = mfree(arg_confirm_spawn); + arg_join_controllers = strv_free_free(arg_join_controllers); ++ arg_show_status = _SHOW_STATUS_UNSET; ++ arg_switched_root = false; ++ arg_no_pager = false; ++ arg_service_watchdogs = true; ++ arg_default_std_output = EXEC_OUTPUT_JOURNAL; ++ arg_default_std_error = EXEC_OUTPUT_INHERIT; ++ arg_default_restart_usec = DEFAULT_RESTART_USEC; ++ arg_default_timeout_start_usec = DEFAULT_TIMEOUT_USEC; ++ arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC; ++ arg_default_timeout_abort_usec = DEFAULT_TIMEOUT_USEC; ++ arg_default_timeout_abort_set = false; ++ arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL; ++ arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST; ++ arg_runtime_watchdog = 0; ++ arg_shutdown_watchdog = 10 * USEC_PER_MINUTE; ++ arg_early_core_pattern = NULL; ++ arg_watchdog_device = NULL; ++ + arg_default_environment = strv_free(arg_default_environment); ++ rlimit_free_all(arg_default_rlimit); ++ ++ arg_capability_bounding_set = CAP_ALL; ++ arg_no_new_privs = false; ++ arg_timer_slack_nsec = NSEC_INFINITY; ++ arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE; ++ + arg_syscall_archs = set_free(arg_syscall_archs); + ++ /* arg_serialization — ignore */ ++ ++ arg_default_cpu_accounting = -1; ++ arg_default_io_accounting = false; ++ arg_default_ip_accounting = false; ++ arg_default_blockio_accounting = false; ++ arg_default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT; ++ arg_default_tasks_accounting = true; ++ arg_default_tasks_max = UINT64_MAX; ++ arg_machine_id = (sd_id128_t) {}; ++ arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; ++ + cpu_set_reset(&arg_cpu_affinity); + } + +@@ -1970,6 +2018,9 @@ static int parse_configuration(void) { + + arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); + ++ /* Assign configuration defaults */ ++ reset_arguments(); ++ + r = parse_config_file(); + if (r < 0) + log_warning_errno(r, "Failed to parse config file, ignoring: %m"); +@@ -2460,7 +2511,7 @@ finish: + m = manager_free(m); + } + +- free_arguments(); ++ reset_arguments(); + mac_selinux_finish(); + + if (reexecute) diff --git a/SOURCES/0279-test-execute-use-CPUSet-too.patch b/SOURCES/0279-test-execute-use-CPUSet-too.patch new file mode 100644 index 0000000..2332226 --- /dev/null +++ b/SOURCES/0279-test-execute-use-CPUSet-too.patch @@ -0,0 +1,114 @@ +From 5e6b616ed2708391752ba8c45f183ceb38573d7d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 28 May 2019 21:38:41 +0200 +Subject: [PATCH] test-execute: use CPUSet too + +cpu_set_malloc() was the last user. It doesn't seem useful to keep +it just to save the allocation of a few hundred bytes in a test, so +it is dropped and a fixed maximum is allocated (1024 bytes). + +(cherry picked from commit 167a776dbe9d033523bd6881e5a695f2155dc321) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 31 +------------------------------ + src/basic/cpu-set-util.h | 3 +-- + src/test/test-execute.c | 13 ++++++------- + 3 files changed, 8 insertions(+), 39 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 1803539ac6..c297eab032 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -37,36 +37,7 @@ char* cpu_set_to_string(const CPUSet *a) { + return TAKE_PTR(str) ?: strdup(""); + } + +-cpu_set_t* cpu_set_malloc(unsigned *ncpus) { +- cpu_set_t *c; +- unsigned n = 1024; +- +- /* Allocates the cpuset in the right size */ +- +- for (;;) { +- c = CPU_ALLOC(n); +- if (!c) +- return NULL; +- +- if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) { +- CPU_ZERO_S(CPU_ALLOC_SIZE(n), c); +- +- if (ncpus) +- *ncpus = n; +- +- return c; +- } +- +- CPU_FREE(c); +- +- if (errno != EINVAL) +- return NULL; +- +- n *= 2; +- } +-} +- +-static int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) { ++int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) { + size_t need; + + assert(cpu_set); +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 9b026aca09..b54e737110 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -12,8 +12,6 @@ + DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); + #define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) + +-cpu_set_t* cpu_set_malloc(unsigned *ncpus); +- + /* This wraps the libc interface with a variable to keep the allocated size. */ + typedef struct CPUSet { + cpu_set_t *set; +@@ -30,6 +28,7 @@ static inline void cpu_set_reset(CPUSet *a) { + int cpu_set_add_all(CPUSet *a, const CPUSet *b); + + char* cpu_set_to_string(const CPUSet *a); ++int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus); + int parse_cpu_set_full( + const char *rvalue, + CPUSet *cpu_set, +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index fa8efdddd2..6c22995b1e 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -144,13 +144,12 @@ static void test_exec_bindpaths(Manager *m) { + } + + static void test_exec_cpuaffinity(Manager *m) { +- _cleanup_cpu_free_ cpu_set_t *c = NULL; +- unsigned n; ++ _cleanup_(cpu_set_reset) CPUSet c = {}; + +- assert_se(c = cpu_set_malloc(&n)); +- assert_se(sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0); ++ assert_se(cpu_set_realloc(&c, 8192) >= 0); /* just allocate the maximum possible size */ ++ assert_se(sched_getaffinity(0, c.allocated, c.set) >= 0); + +- if (CPU_ISSET_S(0, CPU_ALLOC_SIZE(n), c) == 0) { ++ if (!CPU_ISSET_S(0, c.allocated, c.set)) { + log_notice("Cannot use CPU 0, skipping %s", __func__); + return; + } +@@ -158,8 +157,8 @@ static void test_exec_cpuaffinity(Manager *m) { + test(m, "exec-cpuaffinity1.service", 0, CLD_EXITED); + test(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) { ++ if (!CPU_ISSET_S(1, c.allocated, c.set) || ++ !CPU_ISSET_S(2, c.allocated, c.set)) { + log_notice("Cannot use CPU 1 or 2, skipping remaining tests in %s", __func__); + return; + } diff --git a/SOURCES/0280-shared-cpu-set-util-drop-now-unused-cleanup-function.patch b/SOURCES/0280-shared-cpu-set-util-drop-now-unused-cleanup-function.patch new file mode 100644 index 0000000..21bf12e --- /dev/null +++ b/SOURCES/0280-shared-cpu-set-util-drop-now-unused-cleanup-function.patch @@ -0,0 +1,26 @@ +From 7aa32093c3dfc4bf7298f02be553e95c40d7c211 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 28 May 2019 21:40:10 +0200 +Subject: [PATCH] shared/cpu-set-util: drop now-unused cleanup function + +(cherry picked from commit cb0d3acf55ef335001cac5dd9c335ec5e75e9b56) + +Related: #1734787 +--- + src/basic/cpu-set-util.h | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index b54e737110..68a73bf9f7 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -9,9 +9,6 @@ + + #include "macro.h" + +-DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); +-#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) +- + /* This wraps the libc interface with a variable to keep the allocated size. */ + typedef struct CPUSet { + cpu_set_t *set; diff --git a/SOURCES/0281-shared-cpu-set-util-make-transfer-of-cpu_set_t-over-.patch b/SOURCES/0281-shared-cpu-set-util-make-transfer-of-cpu_set_t-over-.patch new file mode 100644 index 0000000..d09f290 --- /dev/null +++ b/SOURCES/0281-shared-cpu-set-util-make-transfer-of-cpu_set_t-over-.patch @@ -0,0 +1,126 @@ +From daa0243fda679c8af723648b8b1e501fc55b0ada Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 22 May 2019 13:55:49 +0200 +Subject: [PATCH] shared/cpu-set-util: make transfer of cpu_set_t over bus + endian safe + +(cherry picked from commit c367f996f5f091a63f812f0140b304c649be77fc) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 38 ++++++++++++++++++++++++++++++++++++++ + src/basic/cpu-set-util.h | 3 +++ + src/core/dbus-execute.c | 6 +++++- + src/shared/bus-unit-util.c | 8 +++++++- + 4 files changed, 53 insertions(+), 2 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index c297eab032..74e35e57dd 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -209,3 +209,41 @@ int cpus_in_affinity_mask(void) { + n *= 2; + } + } ++ ++int cpu_set_to_dbus(const CPUSet *set, uint8_t **ret, size_t *allocated) { ++ uint8_t *out; ++ ++ assert(set); ++ assert(ret); ++ ++ out = new0(uint8_t, set->allocated); ++ if (!out) ++ return -ENOMEM; ++ ++ for (unsigned cpu = 0; cpu < set->allocated * 8; cpu++) ++ if (CPU_ISSET_S(cpu, set->allocated, set->set)) ++ out[cpu / 8] |= 1u << (cpu % 8); ++ ++ *ret = out; ++ *allocated = set->allocated; ++ return 0; ++} ++ ++int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set) { ++ _cleanup_(cpu_set_reset) CPUSet s = {}; ++ int r; ++ ++ assert(bits); ++ assert(set); ++ ++ for (unsigned cpu = size * 8; cpu > 0; cpu--) ++ if (bits[(cpu - 1) / 8] & (1u << ((cpu - 1) % 8))) { ++ r = cpu_set_add(&s, cpu - 1); ++ if (r < 0) ++ return r; ++ } ++ ++ *set = s; ++ s = (CPUSet) {}; ++ return 0; ++} +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 68a73bf9f7..415c6ca295 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -46,4 +46,7 @@ static inline int parse_cpu_set(const char *rvalue, CPUSet *cpu_set){ + return parse_cpu_set_full(rvalue, cpu_set, false, NULL, NULL, 0, NULL); + } + ++int cpu_set_to_dbus(const CPUSet *set, uint8_t **ret, size_t *allocated); ++int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set); ++ + int cpus_in_affinity_mask(void); +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 08946627e3..50ea71a281 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1553,18 +1553,22 @@ int bus_exec_context_set_transient_property( + if (streq(name, "CPUAffinity")) { + const void *a; + size_t n; ++ _cleanup_(cpu_set_reset) CPUSet set = {}; + + r = sd_bus_message_read_array(message, 'y', &a, &n); + if (r < 0) + return r; + ++ r = cpu_set_from_dbus(a, n, &set); ++ if (r < 0) ++ return r; ++ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (n == 0) { + cpu_set_reset(&c->cpu_set); + unit_write_settingf(u, flags, name, "%s=", name); + } else { + _cleanup_free_ char *str = NULL; +- const CPUSet set = {(cpu_set_t*) a, n}; + + str = cpu_set_to_string(&set); + if (!str) +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 75b4aace84..ec8732c226 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -933,12 +933,18 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + + if (streq(field, "CPUAffinity")) { + _cleanup_(cpu_set_reset) CPUSet cpuset = {}; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; + + r = parse_cpu_set(eq, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + +- return bus_append_byte_array(m, field, cpuset.set, cpuset.allocated); ++ r = cpu_set_to_dbus(&cpuset, &array, &allocated); ++ if (r < 0) ++ return log_error_errno(r, "Failed to serialize CPUAffinity: %m"); ++ ++ return bus_append_byte_array(m, field, array, allocated); + } + + if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) { diff --git a/SOURCES/0282-test-cpu-set-util-add-test-for-dbus-conversions.patch b/SOURCES/0282-test-cpu-set-util-add-test-for-dbus-conversions.patch new file mode 100644 index 0000000..f6e259a --- /dev/null +++ b/SOURCES/0282-test-cpu-set-util-add-test-for-dbus-conversions.patch @@ -0,0 +1,61 @@ +From fd65eadbbcc068171ee9164610fd1c2016b3bf59 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 29 May 2019 09:44:16 +0200 +Subject: [PATCH] test-cpu-set-util: add test for dbus conversions + +(cherry picked from commit 1bf0d6c28f8c884e187c7dacc1a969c0763ff4e3) + +Related: #1734787 +--- + src/test/test-cpu-set-util.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 81f67647e8..cae51ad7d9 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -181,6 +181,36 @@ static void test_parse_cpu_set_extend(void) { + log_info("cpu_set_to_string: (null)"); + } + ++static void test_cpu_set_to_from_dbus(void) { ++ _cleanup_(cpu_set_reset) CPUSet c = {}, c2 = {}; ++ _cleanup_free_ char *s = NULL; ++ ++ log_info("/* %s */", __func__); ++ ++ assert_se(parse_cpu_set_extend("1 3 8 100-200", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(s = cpu_set_to_string(&c)); ++ log_info("cpu_set_to_string: %s", s); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 104); ++ ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ static const char expected[32] = ++ "\x0A\x01\x00\x00\x00\x00\x00\x00\x00\x00" ++ "\x00\x00\xF0\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ++ "\xFF\xFF\xFF\xFF\xFF\x01"; ++ ++ assert_se(cpu_set_to_dbus(&c, &array, &allocated) == 0); ++ assert_se(array); ++ assert_se(allocated == c.allocated); ++ ++ assert(memcmp(array, expected, sizeof expected) == 0); ++ ++ assert_se(cpu_set_from_dbus(array, allocated, &c2) == 0); ++ assert_se(c2.set); ++ assert_se(c2.allocated == c.allocated); ++ assert_se(memcmp(c.set, c2.set, c.allocated) == 0); ++} ++ + static void test_cpus_in_affinity_mask(void) { + int r; + +@@ -201,6 +231,7 @@ int main(int argc, char *argv[]) { + test_parse_cpu_set(); + test_parse_cpu_set_extend(); + test_cpus_in_affinity_mask(); ++ test_cpu_set_to_from_dbus(); + + return 0; + } diff --git a/SOURCES/0283-shared-cpu-set-util-introduce-cpu_set_to_range.patch b/SOURCES/0283-shared-cpu-set-util-introduce-cpu_set_to_range.patch new file mode 100644 index 0000000..d49553f --- /dev/null +++ b/SOURCES/0283-shared-cpu-set-util-introduce-cpu_set_to_range.patch @@ -0,0 +1,203 @@ +From 93777a6dd8c12d5cba094694bf7ed6e8c06c2d6d Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 23 May 2019 14:27:18 +0200 +Subject: [PATCH] shared/cpu-set-util: introduce cpu_set_to_range() + +(cherry picked from commit 71b28519b55b496237146f9bcb5a627455f15f7e) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 37 ++++++++++++++++++++++++++ + src/basic/cpu-set-util.h | 2 ++ + src/test/test-cpu-set-util.c | 50 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 89 insertions(+) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 74e35e57dd..bff39ec143 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -37,6 +37,43 @@ char* cpu_set_to_string(const CPUSet *a) { + return TAKE_PTR(str) ?: strdup(""); + } + ++char *cpu_set_to_range_string(const CPUSet *set) { ++ unsigned range_start = 0, range_end; ++ _cleanup_free_ char *str = NULL; ++ size_t allocated = 0, len = 0; ++ bool in_range = false; ++ int r; ++ ++ for (unsigned i = 0; i < set->allocated * 8; i++) ++ if (CPU_ISSET_S(i, set->allocated, set->set)) { ++ if (in_range) ++ range_end++; ++ else { ++ range_start = range_end = i; ++ in_range = true; ++ } ++ } else if (in_range) { ++ in_range = false; ++ ++ if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(unsigned))) ++ return NULL; ++ ++ r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); ++ assert_se(r > 0); ++ len += r; ++ } ++ ++ if (in_range) { ++ if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(int))) ++ return NULL; ++ ++ r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); ++ assert_se(r > 0); ++ } ++ ++ return TAKE_PTR(str) ?: strdup(""); ++} ++ + int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) { + size_t need; + +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 415c6ca295..ec640b2ec9 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -25,7 +25,9 @@ static inline void cpu_set_reset(CPUSet *a) { + int cpu_set_add_all(CPUSet *a, const CPUSet *b); + + char* cpu_set_to_string(const CPUSet *a); ++char *cpu_set_to_range_string(const CPUSet *a); + int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus); ++ + int parse_cpu_set_full( + const char *rvalue, + CPUSet *cpu_set, +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index cae51ad7d9..0d2741cd43 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -4,6 +4,7 @@ + + #include "alloc-util.h" + #include "cpu-set-util.h" ++#include "string-util.h" + #include "macro.h" + + static void test_parse_cpu_set(void) { +@@ -13,6 +14,22 @@ static void test_parse_cpu_set(void) { + + log_info("/* %s */", __func__); + ++ /* Single value */ ++ assert_se(parse_cpu_set_full("0", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(c.set); ++ assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(CPU_ISSET_S(0, c.allocated, c.set)); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 1); ++ ++ assert_se(str = cpu_set_to_string(&c)); ++ log_info("cpu_set_to_string: %s", str); ++ str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "0-0")); ++ str = mfree(str); ++ cpu_set_reset(&c); ++ + /* Simple range (from CPUAffinity example) */ + assert_se(parse_cpu_set_full("1 2", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.set); +@@ -24,6 +41,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "1-2")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* A more interesting range */ +@@ -34,9 +55,14 @@ static void test_parse_cpu_set(void) { + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "0-3 8-11")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* Quoted strings */ +@@ -48,6 +74,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "8-11")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* Use commas as separators */ +@@ -72,6 +102,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "0-7")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* Ranges */ +@@ -98,6 +132,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "0-3 8-11")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* Negative range (returns empty cpu_set) */ +@@ -115,6 +153,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "0-11")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* Mix ranges and individual CPUs */ +@@ -128,6 +170,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "0-1 4-11")); ++ str = mfree(str); + cpu_set_reset(&c); + + /* Garbage */ +@@ -156,6 +202,10 @@ static void test_parse_cpu_set(void) { + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); ++ assert_se(str = cpu_set_to_range_string(&c)); ++ log_info("cpu_set_to_range_string: %s", str); ++ assert_se(streq(str, "8000-8191")); ++ str = mfree(str); + cpu_set_reset(&c); + } + diff --git a/SOURCES/0284-systemctl-present-CPUAffinity-mask-as-a-list-of-CPU-.patch b/SOURCES/0284-systemctl-present-CPUAffinity-mask-as-a-list-of-CPU-.patch new file mode 100644 index 0000000..44d35dd --- /dev/null +++ b/SOURCES/0284-systemctl-present-CPUAffinity-mask-as-a-list-of-CPU-.patch @@ -0,0 +1,53 @@ +From fb1244ef318e9f54628a7c13db9e2ffbc712dd38 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 22 May 2019 17:14:21 +0200 +Subject: [PATCH] systemctl: present CPUAffinity mask as a list of CPU index + ranges + +(cherry picked from commit a047f4f10ed2f922d6079c033d24a443b0e95f38) + +Related: #1734787 +--- + src/systemctl/systemctl.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index f072ad0c31..0154b300a3 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -30,6 +30,7 @@ + #include "cgroup-show.h" + #include "cgroup-util.h" + #include "copy.h" ++#include "cpu-set-util.h" + #include "dropin.h" + #include "efivars.h" + #include "env-util.h" +@@ -4876,6 +4877,27 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool + + print_prop(name, "%s", h); + ++ return 1; ++ } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "CPUAffinity")) { ++ _cleanup_free_ char *affinity = NULL; ++ _cleanup_(cpu_set_reset) CPUSet set = {}; ++ const void *a; ++ size_t n; ++ ++ r = sd_bus_message_read_array(m, 'y', &a, &n); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ r = cpu_set_from_dbus(a, n, &set); ++ if (r < 0) ++ return log_error_errno(r, "Failed to deserialize CPUAffinity: %m"); ++ ++ affinity = cpu_set_to_range_string(&set); ++ if (!affinity) ++ return log_oom(); ++ ++ print_prop(name, "%s", affinity); ++ + return 1; + } + diff --git a/SOURCES/0285-shared-cpu-set-util-only-force-range-printing-one-ti.patch b/SOURCES/0285-shared-cpu-set-util-only-force-range-printing-one-ti.patch new file mode 100644 index 0000000..5f34600 --- /dev/null +++ b/SOURCES/0285-shared-cpu-set-util-only-force-range-printing-one-ti.patch @@ -0,0 +1,77 @@ +From cabd9055d0d745f7de9625dec6c623d363dd3aa6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 29 May 2019 10:17:43 +0200 +Subject: [PATCH] shared/cpu-set-util: only force range printing one time +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The idea is to have at least one range to make the new format clearly +distinguishable from the old. But it is enough to just do it once. +In particular, in case the affinity would be specified like 0, 2, 4, 6…, +this gives much shorter output. + +(cherry picked from commit 1f57a176af5152d05719bf43740e87a47e37af50) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 10 ++++++++-- + src/test/test-cpu-set-util.c | 7 ++++--- + 2 files changed, 12 insertions(+), 5 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index bff39ec143..5024290557 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -58,7 +58,10 @@ char *cpu_set_to_range_string(const CPUSet *set) { + if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(unsigned))) + return NULL; + +- r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); ++ if (range_end > range_start || len == 0) ++ r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); ++ else ++ r = sprintf(str + len, len > 0 ? " %d" : "%d", range_start); + assert_se(r > 0); + len += r; + } +@@ -67,7 +70,10 @@ char *cpu_set_to_range_string(const CPUSet *set) { + if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(int))) + return NULL; + +- r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); ++ if (range_end > range_start || len == 0) ++ r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); ++ else ++ r = sprintf(str + len, len > 0 ? " %d" : "%d", range_start); + assert_se(r > 0); + } + +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 0d2741cd43..995b981d25 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -31,19 +31,20 @@ static void test_parse_cpu_set(void) { + cpu_set_reset(&c); + + /* Simple range (from CPUAffinity example) */ +- assert_se(parse_cpu_set_full("1 2", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(parse_cpu_set_full("1 2 4", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.set); + assert_se(c.allocated >= sizeof(__cpu_mask) / 8); + assert_se(CPU_ISSET_S(1, c.allocated, c.set)); + assert_se(CPU_ISSET_S(2, c.allocated, c.set)); +- assert_se(CPU_COUNT_S(c.allocated, c.set) == 2); ++ assert_se(CPU_ISSET_S(4, c.allocated, c.set)); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 3); + + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); + assert_se(str = cpu_set_to_range_string(&c)); + log_info("cpu_set_to_range_string: %s", str); +- assert_se(streq(str, "1-2")); ++ assert_se(streq(str, "1-2 4")); + str = mfree(str); + cpu_set_reset(&c); + diff --git a/SOURCES/0286-execute-dump-CPUAffinity-as-a-range-string-instead-o.patch b/SOURCES/0286-execute-dump-CPUAffinity-as-a-range-string-instead-o.patch new file mode 100644 index 0000000..050478a --- /dev/null +++ b/SOURCES/0286-execute-dump-CPUAffinity-as-a-range-string-instead-o.patch @@ -0,0 +1,36 @@ +From b90f935f8d2268522480a7c12f7e2213a7a5e19d Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 31 May 2019 18:02:20 +0200 +Subject: [PATCH] execute: dump CPUAffinity as a range string instead of a list + of CPUs + +We do this already when printing the property in systemctl so be +consistent and do the same for systemd-analyze dump. + +(cherry picked from commit e7fca352ba43988682a927de6b1f629b3f10a415) + +Related: #1734787 +--- + src/core/execute.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 22e5825905..bc26aa66e7 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -4098,11 +4098,10 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { + } + + if (c->cpu_set.set) { +- fprintf(f, "%sCPUAffinity:", prefix); +- for (i = 0; i < c->cpu_set.allocated * 8; i++) +- if (CPU_ISSET_S(i, c->cpu_set.allocated, c->cpu_set.set)) +- fprintf(f, " %u", i); +- fputs("\n", f); ++ _cleanup_free_ char *affinity = NULL; ++ ++ affinity = cpu_set_to_range_string(&c->cpu_set); ++ fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity); + } + + if (c->timer_slack_nsec != NSEC_INFINITY) diff --git a/SOURCES/0287-cpu-set-util-use-d-d-format-in-cpu_set_to_range_stri.patch b/SOURCES/0287-cpu-set-util-use-d-d-format-in-cpu_set_to_range_stri.patch new file mode 100644 index 0000000..0b2bec0 --- /dev/null +++ b/SOURCES/0287-cpu-set-util-use-d-d-format-in-cpu_set_to_range_stri.patch @@ -0,0 +1,95 @@ +From 35894625624f0e8c7d3ca2c200861005c7ad4435 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Mon, 3 Jun 2019 10:12:35 +0200 +Subject: [PATCH] cpu-set-util: use %d-%d format in cpu_set_to_range_string() + only for actual ranges + +(cherry picked from commit 71923237b18df35401626993d8a285cd998be78d) + +Related: #1734787 +--- + src/basic/cpu-set-util.c | 4 ++-- + src/test/test-cpu-set-util.c | 16 +++++++++------- + 2 files changed, 11 insertions(+), 9 deletions(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 5024290557..103b9703b3 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -58,7 +58,7 @@ char *cpu_set_to_range_string(const CPUSet *set) { + if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(unsigned))) + return NULL; + +- if (range_end > range_start || len == 0) ++ if (range_end > range_start) + r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); + else + r = sprintf(str + len, len > 0 ? " %d" : "%d", range_start); +@@ -70,7 +70,7 @@ char *cpu_set_to_range_string(const CPUSet *set) { + if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(int))) + return NULL; + +- if (range_end > range_start || len == 0) ++ if (range_end > range_start) + r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end); + else + r = sprintf(str + len, len > 0 ? " %d" : "%d", range_start); +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 995b981d25..9522582891 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -26,7 +26,7 @@ static void test_parse_cpu_set(void) { + str = mfree(str); + assert_se(str = cpu_set_to_range_string(&c)); + log_info("cpu_set_to_range_string: %s", str); +- assert_se(streq(str, "0-0")); ++ assert_se(streq(str, "0")); + str = mfree(str); + cpu_set_reset(&c); + +@@ -95,17 +95,19 @@ static void test_parse_cpu_set(void) { + cpu_set_reset(&c); + + /* Commas with spaces (and trailing comma, space) */ +- assert_se(parse_cpu_set_full("0, 1, 2, 3, 4, 5, 6, 7, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(parse_cpu_set_full("0, 1, 2, 3, 4, 5, 6, 7, 63, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.allocated >= sizeof(__cpu_mask) / 8); +- assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); ++ assert_se(CPU_COUNT_S(c.allocated, c.set) == 9); + for (cpu = 0; cpu < 8; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); ++ ++ assert_se(CPU_ISSET_S(63, c.allocated, c.set)); + assert_se(str = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", str); + str = mfree(str); + assert_se(str = cpu_set_to_range_string(&c)); + log_info("cpu_set_to_range_string: %s", str); +- assert_se(streq(str, "0-7")); ++ assert_se(streq(str, "0-7 63")); + str = mfree(str); + cpu_set_reset(&c); + +@@ -161,11 +163,11 @@ static void test_parse_cpu_set(void) { + cpu_set_reset(&c); + + /* Mix ranges and individual CPUs */ +- assert_se(parse_cpu_set_full("0,1 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); ++ assert_se(parse_cpu_set_full("0,2 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.allocated >= sizeof(__cpu_mask) / 8); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 10); + assert_se(CPU_ISSET_S(0, c.allocated, c.set)); +- assert_se(CPU_ISSET_S(1, c.allocated, c.set)); ++ assert_se(CPU_ISSET_S(2, c.allocated, c.set)); + for (cpu = 4; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); + assert_se(str = cpu_set_to_string(&c)); +@@ -173,7 +175,7 @@ static void test_parse_cpu_set(void) { + str = mfree(str); + assert_se(str = cpu_set_to_range_string(&c)); + log_info("cpu_set_to_range_string: %s", str); +- assert_se(streq(str, "0-1 4-11")); ++ assert_se(streq(str, "0 2 4-11")); + str = mfree(str); + cpu_set_reset(&c); + diff --git a/SOURCES/0288-core-introduce-NUMAPolicy-and-NUMAMask-options.patch b/SOURCES/0288-core-introduce-NUMAPolicy-and-NUMAMask-options.patch new file mode 100644 index 0000000..bcfd3a3 --- /dev/null +++ b/SOURCES/0288-core-introduce-NUMAPolicy-and-NUMAMask-options.patch @@ -0,0 +1,779 @@ +From a735699a8287c19e043b7d2fe9a387a3938e1e2f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 18 Nov 2019 12:50:11 +0100 +Subject: [PATCH] core: introduce NUMAPolicy and NUMAMask options + +Make possible to set NUMA allocation policy for manager. Manager's +policy is by default inherited to all forked off processes. However, it +is possible to override the policy on per-service basis. Currently we +support, these policies: default, prefer, bind, interleave, local. +See man 2 set_mempolicy for details on each policy. + +Overall NUMA policy actually consists of two parts. Policy itself and +bitmask representing NUMA nodes where is policy effective. Node mask can +be specified using related option, NUMAMask. Default mask can be +overwritten on per-service level. + +(cherry-picked from commit fe9c54b2188e6cd23262a319f96b13215f2c5e9c) + +Resolves: #1734787 +--- + man/systemd-system.conf.xml | 19 ++++++ + man/systemd.exec.xml | 28 +++++++++ + meson.build | 4 ++ + src/basic/cpu-set-util.c | 91 +++++++++++++++++++++++++++ + src/basic/cpu-set-util.h | 28 +++++++++ + src/basic/exit-status.c | 3 + + src/basic/exit-status.h | 1 + + src/basic/missing_syscall.h | 43 +++++++++++++ + src/core/dbus-execute.c | 65 ++++++++++++++++++- + src/core/execute.c | 20 ++++++ + src/core/execute.h | 1 + + src/core/load-fragment-gperf.gperf.m4 | 2 + + src/core/load-fragment.c | 28 +++++++++ + src/core/load-fragment.h | 2 + + src/core/main.c | 27 ++++++++ + src/core/system.conf.in | 2 + + src/shared/bus-unit-util.c | 28 +++++++++ + src/systemctl/systemctl.c | 18 +++++- + 18 files changed, 405 insertions(+), 5 deletions(-) + +diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml +index ab23779ec0..988c4e7665 100644 +--- a/man/systemd-system.conf.xml ++++ b/man/systemd-system.conf.xml +@@ -132,6 +132,25 @@ + anymore. + + ++ ++ NUMAPolicy= ++ ++ Configures the NUMA memory policy for the service manager and the default NUMA memory policy ++ for all forked off processes. Individual services may override the default policy with the ++ NUMAPolicy= setting in unit files, see ++ systemd.exec5. ++ ++ ++ ++ NUMAMask= ++ ++ Configures the NUMA node mask that will be associated with the selected NUMA policy. Note that ++ and NUMA policies don't require explicit NUMA node mask and ++ value of the option can be empty. Similarly to NUMAPolicy=, value can be overriden ++ by individual services in unit files, see ++ systemd.exec5. ++ ++ + + RuntimeWatchdogSec= + ShutdownWatchdogSec= +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index 342b8385bc..87fb8b34f4 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -710,6 +710,28 @@ CapabilityBoundingSet=~CAP_B CAP_C + details. + + ++ ++ NUMAPolicy= ++ ++ Controls the NUMA memory policy of the executed processes. Takes a policy type, one of: ++ , , , and ++ . A list of NUMA nodes that should be associated with the policy must be specified ++ in NUMAMask=. For more details on each policy please see, ++ set_mempolicy2. For overall ++ overview of NUMA support in Linux see, ++ numa7 ++ ++ ++ ++ ++ NUMAMask= ++ ++ Controls the NUMA node list which will be applied alongside with selected NUMA policy. ++ Takes a list of NUMA nodes and has the same syntax as a list of CPUs for CPUAffinity= ++ option. Note that the list of NUMA nodes is not required for and ++ policies and for policy we expect a single NUMA node. ++ ++ + + IOSchedulingClass= + +@@ -2709,6 +2731,12 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + EXIT_CONFIGURATION_DIRECTORY + Failed to set up unit's configuration directory. See ConfigurationDirectory= above. + ++ ++ 242 ++ EXIT_NUMA_POLICY ++ Failed to set up unit's NUMA memory policy. See NUMAPolicy= and NUMAMask=above. ++ ++ + + + +diff --git a/meson.build b/meson.build +index 613a5133b6..fe82ca4ac2 100644 +--- a/meson.build ++++ b/meson.build +@@ -501,6 +501,10 @@ foreach ident : [ + #include '''], + ['explicit_bzero' , '''#include '''], + ['reallocarray', '''#include '''], ++ ['set_mempolicy', '''#include ++ #include '''], ++ ['get_mempolicy', '''#include ++ #include '''], + ] + + have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE') +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 103b9703b3..36cb017ae7 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -10,11 +10,17 @@ + + #include "alloc-util.h" + #include "cpu-set-util.h" ++#include "dirent-util.h" + #include "extract-word.h" ++#include "fd-util.h" + #include "log.h" + #include "macro.h" ++#include "missing.h" + #include "parse-util.h" ++#include "stat-util.h" + #include "string-util.h" ++#include "string-table.h" ++#include "strv.h" + #include "util.h" + + char* cpu_set_to_string(const CPUSet *a) { +@@ -290,3 +296,88 @@ int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set) { + s = (CPUSet) {}; + return 0; + } ++ ++bool numa_policy_is_valid(const NUMAPolicy *policy) { ++ assert(policy); ++ ++ if (!mpol_is_valid(numa_policy_get_type(policy))) ++ return false; ++ ++ if (!policy->nodes.set && ++ !IN_SET(numa_policy_get_type(policy), MPOL_DEFAULT, MPOL_LOCAL, MPOL_PREFERRED)) ++ return false; ++ ++ if (policy->nodes.set && ++ numa_policy_get_type(policy) == MPOL_PREFERRED && ++ CPU_COUNT_S(policy->nodes.allocated, policy->nodes.set) != 1) ++ return false; ++ ++ return true; ++} ++ ++static int numa_policy_to_mempolicy(const NUMAPolicy *policy, unsigned long *ret_maxnode, unsigned long **ret_nodes) { ++ unsigned node, bits = 0, ulong_bits; ++ _cleanup_free_ unsigned long *out = NULL; ++ ++ assert(policy); ++ assert(ret_maxnode); ++ assert(ret_nodes); ++ ++ if (IN_SET(numa_policy_get_type(policy), MPOL_DEFAULT, MPOL_LOCAL) || ++ (numa_policy_get_type(policy) == MPOL_PREFERRED && !policy->nodes.set)) { ++ *ret_nodes = NULL; ++ *ret_maxnode = 0; ++ return 0; ++ } ++ ++ bits = policy->nodes.allocated * 8; ++ ulong_bits = sizeof(unsigned long) * 8; ++ ++ out = new0(unsigned long, DIV_ROUND_UP(policy->nodes.allocated, sizeof(unsigned long))); ++ if (!out) ++ return -ENOMEM; ++ ++ /* We don't make any assumptions about internal type libc is using to store NUMA node mask. ++ Hence we need to convert the node mask to the representation expected by set_mempolicy() */ ++ for (node = 0; node < bits; node++) ++ if (CPU_ISSET_S(node, policy->nodes.allocated, policy->nodes.set)) ++ out[node / ulong_bits] |= 1ul << (node % ulong_bits); ++ ++ *ret_nodes = TAKE_PTR(out); ++ *ret_maxnode = bits + 1; ++ return 0; ++} ++ ++int apply_numa_policy(const NUMAPolicy *policy) { ++ int r; ++ _cleanup_free_ unsigned long *nodes = NULL; ++ unsigned long maxnode; ++ ++ assert(policy); ++ ++ if (get_mempolicy(NULL, NULL, 0, 0, 0) < 0 && errno == ENOSYS) ++ return -EOPNOTSUPP; ++ ++ if (!numa_policy_is_valid(policy)) ++ return -EINVAL; ++ ++ r = numa_policy_to_mempolicy(policy, &maxnode, &nodes); ++ if (r < 0) ++ return r; ++ ++ r = set_mempolicy(numa_policy_get_type(policy), nodes, maxnode); ++ if (r < 0) ++ return -errno; ++ ++ return 0; ++} ++ ++static const char* const mpol_table[] = { ++ [MPOL_DEFAULT] = "default", ++ [MPOL_PREFERRED] = "preferred", ++ [MPOL_BIND] = "bind", ++ [MPOL_INTERLEAVE] = "interleave", ++ [MPOL_LOCAL] = "local", ++}; ++ ++DEFINE_STRING_TABLE_LOOKUP(mpol, int); +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index ec640b2ec9..295028cb54 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -8,6 +8,7 @@ + #include + + #include "macro.h" ++#include "missing.h" + + /* This wraps the libc interface with a variable to keep the allocated size. */ + typedef struct CPUSet { +@@ -52,3 +53,30 @@ int cpu_set_to_dbus(const CPUSet *set, uint8_t **ret, size_t *allocated); + int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set); + + int cpus_in_affinity_mask(void); ++ ++static inline bool mpol_is_valid(int t) { ++ return t >= MPOL_DEFAULT && t <= MPOL_LOCAL; ++} ++ ++typedef struct NUMAPolicy { ++ /* Always use numa_policy_get_type() to read the value */ ++ int type; ++ CPUSet nodes; ++} NUMAPolicy; ++ ++bool numa_policy_is_valid(const NUMAPolicy *p); ++ ++static inline int numa_policy_get_type(const NUMAPolicy *p) { ++ return p->type < 0 ? (p->nodes.set ? MPOL_PREFERRED : -1) : p->type; ++} ++ ++static inline void numa_policy_reset(NUMAPolicy *p) { ++ assert(p); ++ cpu_set_reset(&p->nodes); ++ p->type = -1; ++} ++ ++int apply_numa_policy(const NUMAPolicy *policy); ++ ++const char* mpol_to_string(int i) _const_; ++int mpol_from_string(const char *s) _pure_; +diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c +index 21af8c4c71..0a7a53b73d 100644 +--- a/src/basic/exit-status.c ++++ b/src/basic/exit-status.c +@@ -155,6 +155,9 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) { + + case EXIT_CONFIGURATION_DIRECTORY: + return "CONFIGURATION_DIRECTORY"; ++ ++ case EXIT_NUMA_POLICY: ++ return "NUMA_POLICY"; + } + } + +diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h +index c41e8b82c3..dc284aacb1 100644 +--- a/src/basic/exit-status.h ++++ b/src/basic/exit-status.h +@@ -69,6 +69,7 @@ enum { + EXIT_CACHE_DIRECTORY, + EXIT_LOGS_DIRECTORY, /* 240 */ + EXIT_CONFIGURATION_DIRECTORY, ++ EXIT_NUMA_POLICY, + }; + + typedef enum ExitStatusLevel { +diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h +index 93c60458bf..014dd2b326 100644 +--- a/src/basic/missing_syscall.h ++++ b/src/basic/missing_syscall.h +@@ -428,3 +428,46 @@ static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flag + + # define statx missing_statx + #endif ++ ++#if !HAVE_SET_MEMPOLICY ++ ++enum { ++ MPOL_DEFAULT, ++ MPOL_PREFERRED, ++ MPOL_BIND, ++ MPOL_INTERLEAVE, ++ MPOL_LOCAL, ++}; ++ ++static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask, ++ unsigned long maxnode) { ++ long i; ++# ifdef __NR_set_mempolicy ++ i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode); ++# else ++ errno = ENOSYS; ++ i = -1; ++# endif ++ return i; ++} ++ ++# define set_mempolicy missing_set_mempolicy ++#endif ++ ++ ++#if !HAVE_GET_MEMPOLICY ++static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, ++ unsigned long maxnode, void *addr, ++ unsigned long flags) { ++ long i; ++# ifdef __NR_get_mempolicy ++ i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags); ++# else ++ errno = ENOSYS; ++ i = -1; ++# endif ++ return i; ++} ++ ++#define get_mempolicy missing_get_mempolicy ++#endif +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 50ea71a281..198f149210 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -223,6 +223,48 @@ static int property_get_cpu_affinity( + return sd_bus_message_append_array(reply, 'y', c->cpu_set.set, c->cpu_set.allocated); + } + ++static int property_get_numa_mask( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ ExecContext *c = userdata; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ ++ assert(bus); ++ assert(reply); ++ assert(c); ++ ++ (void) cpu_set_to_dbus(&c->numa_policy.nodes, &array, &allocated); ++ ++ return sd_bus_message_append_array(reply, 'y', array, allocated); ++} ++ ++static int property_get_numa_policy( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ExecContext *c = userdata; ++ int32_t policy; ++ ++ assert(bus); ++ assert(reply); ++ assert(c); ++ ++ policy = numa_policy_get_type(&c->numa_policy); ++ ++ return sd_bus_message_append_basic(reply, 'i', &policy); ++} ++ + static int property_get_timer_slack_nsec( + sd_bus *bus, + const char *path, +@@ -698,6 +740,8 @@ const sd_bus_vtable bus_exec_vtable[] = { + SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("NUMAPolicy", "i", property_get_numa_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("NUMAMask", "ay", property_get_numa_mask, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -1550,9 +1594,10 @@ int bus_exec_context_set_transient_property( + return 1; + } + #endif +- if (streq(name, "CPUAffinity")) { ++ if (STR_IN_SET(name, "CPUAffinity", "NUMAMask")) { + const void *a; + size_t n; ++ bool affinity = streq(name, "CPUAffinity"); + _cleanup_(cpu_set_reset) CPUSet set = {}; + + r = sd_bus_message_read_array(message, 'y', &a, &n); +@@ -1565,7 +1610,7 @@ int bus_exec_context_set_transient_property( + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (n == 0) { +- cpu_set_reset(&c->cpu_set); ++ cpu_set_reset(affinity ? &c->cpu_set : &c->numa_policy.nodes); + unit_write_settingf(u, flags, name, "%s=", name); + } else { + _cleanup_free_ char *str = NULL; +@@ -1577,7 +1622,7 @@ int bus_exec_context_set_transient_property( + /* We forego any optimizations here, and always create the structure using + * cpu_set_add_all(), because we don't want to care if the existing size we + * got over dbus is appropriate. */ +- r = cpu_set_add_all(&c->cpu_set, &set); ++ r = cpu_set_add_all(affinity ? &c->cpu_set : &c->numa_policy.nodes, &set); + if (r < 0) + return r; + +@@ -1587,6 +1632,20 @@ int bus_exec_context_set_transient_property( + + return 1; + ++ } else if (streq(name, "NUMAPolicy")) { ++ int32_t type; ++ ++ r = sd_bus_message_read(message, "i", &type); ++ if (r < 0) ++ return r; ++ ++ if (!mpol_is_valid(type)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NUMAPolicy value: %i", type); ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) ++ c->numa_policy.type = type; ++ ++ return 1; + } else if (streq(name, "IOSchedulingClass")) { + int32_t q; + +diff --git a/src/core/execute.c b/src/core/execute.c +index bc26aa66e7..56aa89e1ec 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2997,6 +2997,16 @@ static int exec_child( + return log_unit_error_errno(unit, errno, "Failed to set up CPU affinity: %m"); + } + ++ if (mpol_is_valid(numa_policy_get_type(&context->numa_policy))) { ++ r = apply_numa_policy(&context->numa_policy); ++ if (r == -EOPNOTSUPP) ++ log_unit_debug_errno(unit, r, "NUMA support not available, ignoring."); ++ else if (r < 0) { ++ *exit_status = EXIT_NUMA_POLICY; ++ return log_unit_error_errno(unit, r, "Failed to set NUMA memory policy: %m"); ++ } ++ } ++ + if (context->ioprio_set) + if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) { + *exit_status = EXIT_IOPRIO; +@@ -3651,6 +3661,7 @@ void exec_context_init(ExecContext *c) { + assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL); + c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL; + c->log_level_max = -1; ++ numa_policy_reset(&c->numa_policy); + } + + void exec_context_done(ExecContext *c) { +@@ -3695,6 +3706,7 @@ void exec_context_done(ExecContext *c) { + c->n_temporary_filesystems = 0; + + cpu_set_reset(&c->cpu_set); ++ numa_policy_reset(&c->numa_policy); + + c->utmp_id = mfree(c->utmp_id); + c->selinux_context = mfree(c->selinux_context); +@@ -4104,6 +4116,14 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { + fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity); + } + ++ if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) { ++ _cleanup_free_ char *nodes = NULL; ++ ++ nodes = cpu_set_to_range_string(&c->numa_policy.nodes); ++ fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy))); ++ fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes)); ++ } ++ + if (c->timer_slack_nsec != NSEC_INFINITY) + fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec); + +diff --git a/src/core/execute.h b/src/core/execute.h +index e1e7a494cd..b2eb55f8f5 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -150,6 +150,7 @@ struct ExecContext { + int cpu_sched_priority; + + CPUSet cpu_set; ++ NUMAPolicy numa_policy; + + ExecInput std_input; + ExecOutput std_output; +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 1066bcfb8f..cdf4d14c4e 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -36,6 +36,8 @@ $1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, + $1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof($1, exec_context) + $1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork) + $1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context) ++$1.NUMAPolicy, config_parse_numa_policy, 0, offsetof($1, exec_context.numa_policy.type) ++$1.NUMAMask, config_parse_numa_mask, 0, offsetof($1, exec_context.numa_policy) + $1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) + $1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) + $1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 34ae834188..35dd595098 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -93,6 +93,7 @@ DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint + DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight"); + DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares"); + DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag"); ++DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type"); + + int config_parse_unit_deps( + const char *unit, +@@ -1159,6 +1160,33 @@ int config_parse_exec_cpu_sched_policy(const char *unit, + return 0; + } + ++int config_parse_numa_mask(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; ++ NUMAPolicy *p = data; ++ ++ assert(filename); ++ assert(lvalue); ++ assert(rvalue); ++ assert(data); ++ ++ r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue); ++ if (r < 0) { ++ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue); ++ return 0; ++ } ++ ++ return r; ++} ++ + int config_parse_exec_cpu_sched_prio(const char *unit, + const char *filename, + unsigned line, +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index dad281ef72..f2ca1b8ee7 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -102,6 +102,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec); + CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec); + CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields); + CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode); ++CONFIG_PARSER_PROTOTYPE(config_parse_numa_policy); ++CONFIG_PARSER_PROTOTYPE(config_parse_numa_mask); + + /* gperf prototypes */ + const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); +diff --git a/src/core/main.c b/src/core/main.c +index c74dc641c1..83f9dd5878 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -134,6 +134,7 @@ static uint64_t arg_default_tasks_max; + static sd_id128_t arg_machine_id; + static EmergencyAction arg_cad_burst_action; + static CPUSet arg_cpu_affinity; ++static NUMAPolicy arg_numa_policy; + + static int parse_configuration(void); + +@@ -660,6 +661,8 @@ static int parse_config_file(void) { + { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status }, + { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity }, + { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, ++ { "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type }, ++ { "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy }, + { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog }, + { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog }, + { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device }, +@@ -1501,6 +1504,27 @@ static void update_cpu_affinity(bool skip_setup) { + log_warning_errno(errno, "Failed to set CPU affinity: %m"); + } + ++static void update_numa_policy(bool skip_setup) { ++ int r; ++ _cleanup_free_ char *nodes = NULL; ++ const char * policy = NULL; ++ ++ if (skip_setup || !mpol_is_valid(numa_policy_get_type(&arg_numa_policy))) ++ return; ++ ++ if (DEBUG_LOGGING) { ++ policy = mpol_to_string(numa_policy_get_type(&arg_numa_policy)); ++ nodes = cpu_set_to_range_string(&arg_numa_policy.nodes); ++ log_debug("Setting NUMA policy to %s, with nodes %s.", strnull(policy), strnull(nodes)); ++ } ++ ++ r = apply_numa_policy(&arg_numa_policy); ++ if (r == -EOPNOTSUPP) ++ log_debug_errno(r, "NUMA support not available, ignoring."); ++ else if (r < 0) ++ log_warning_errno(r, "Failed to set NUMA memory policy: %m"); ++} ++ + static void do_reexecute( + int argc, + char *argv[], +@@ -1672,6 +1696,7 @@ static int invoke_main_loop( + set_manager_defaults(m); + + update_cpu_affinity(false); ++ update_numa_policy(false); + + if (saved_log_level >= 0) + manager_override_log_level(m, saved_log_level); +@@ -1832,6 +1857,7 @@ static int initialize_runtime( + return 0; + + update_cpu_affinity(skip_setup); ++ update_numa_policy(skip_setup); + + if (arg_system) { + /* Make sure we leave a core dump without panicing the kernel. */ +@@ -2011,6 +2037,7 @@ static void reset_arguments(void) { + arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; + + cpu_set_reset(&arg_cpu_affinity); ++ numa_policy_reset(&arg_numa_policy); + } + + static int parse_configuration(void) { +diff --git a/src/core/system.conf.in b/src/core/system.conf.in +index 653ec6b8c9..0d93fbf147 100644 +--- a/src/core/system.conf.in ++++ b/src/core/system.conf.in +@@ -24,6 +24,8 @@ + #CtrlAltDelBurstAction=reboot-force + #CPUAffinity=1 2 + #JoinControllers=cpu,cpuacct net_cls,net_prio ++#NUMAPolicy=default ++#NUMAMask= + #RuntimeWatchdogSec=0 + #ShutdownWatchdogSec=10min + #CapabilityBoundingSet= +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index ec8732c226..055edd6e22 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -947,6 +947,34 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + return bus_append_byte_array(m, field, array, allocated); + } + ++ if (streq(field, "NUMAPolicy")) { ++ r = mpol_from_string(eq); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse %s value: %s", field, eq); ++ ++ r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ return 1; ++ } ++ ++ if (streq(field, "NUMAMask")) { ++ _cleanup_(cpu_set_reset) CPUSet nodes = {}; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ ++ r = parse_cpu_set(eq, &nodes); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse %s value: %s", field, eq); ++ ++ r = cpu_set_to_dbus(&nodes, &array, &allocated); ++ if (r < 0) ++ return log_error_errno(r, "Failed to serialize NUMAMask: %m"); ++ ++ return bus_append_byte_array(m, field, array, allocated); ++ } ++ + if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) { + int whitelist = 1; + const char *p = eq; +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 0154b300a3..7274921e6d 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -4573,6 +4573,20 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool + + switch (bus_type) { + ++ case SD_BUS_TYPE_INT32: ++ if (streq(name, "NUMAPolicy")) { ++ int32_t i; ++ ++ r = sd_bus_message_read_basic(m, bus_type, &i); ++ if (r < 0) ++ return r; ++ ++ print_prop(name, "%s", strna(mpol_to_string(i))); ++ ++ return 1; ++ } ++ break; ++ + case SD_BUS_TYPE_STRUCT: + + if (contents[0] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) { +@@ -4878,7 +4892,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool + print_prop(name, "%s", h); + + return 1; +- } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "CPUAffinity")) { ++ } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "CPUAffinity", "NUMAMask")) { + _cleanup_free_ char *affinity = NULL; + _cleanup_(cpu_set_reset) CPUSet set = {}; + const void *a; +@@ -4890,7 +4904,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool + + r = cpu_set_from_dbus(a, n, &set); + if (r < 0) +- return log_error_errno(r, "Failed to deserialize CPUAffinity: %m"); ++ return log_error_errno(r, "Failed to deserialize %s: %m", name); + + affinity = cpu_set_to_range_string(&set); + if (!affinity) diff --git a/SOURCES/0289-core-disable-CPUAccounting-by-default.patch b/SOURCES/0289-core-disable-CPUAccounting-by-default.patch new file mode 100644 index 0000000..d250c03 --- /dev/null +++ b/SOURCES/0289-core-disable-CPUAccounting-by-default.patch @@ -0,0 +1,25 @@ +From b47f26558e5234ec8cf2ecc3674c94a87f20ec69 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 28 Nov 2019 15:47:43 +0100 +Subject: [PATCH] core: disable CPUAccounting by default + +Related: #1734787 + +[RHEL-only] +--- + src/core/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/main.c b/src/core/main.c +index 83f9dd5878..c83249a8dc 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -2026,7 +2026,7 @@ static void reset_arguments(void) { + + /* arg_serialization — ignore */ + +- arg_default_cpu_accounting = -1; ++ arg_default_cpu_accounting = 0; + arg_default_io_accounting = false; + arg_default_ip_accounting = false; + arg_default_blockio_accounting = false; diff --git a/SOURCES/0290-set-kptr_restrict-1.patch b/SOURCES/0290-set-kptr_restrict-1.patch new file mode 100644 index 0000000..5005756 --- /dev/null +++ b/SOURCES/0290-set-kptr_restrict-1.patch @@ -0,0 +1,24 @@ +From cf1a9df1171273fc1ed3f977b5ec52aba27674bf Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 3 Dec 2019 14:04:00 +0100 +Subject: [PATCH] set kptr_restrict=1 + +Resolves: #1689346 +--- + sysctl.d/50-default.conf | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/sysctl.d/50-default.conf b/sysctl.d/50-default.conf +index e263cf0628..e0afc9c702 100644 +--- a/sysctl.d/50-default.conf ++++ b/sysctl.d/50-default.conf +@@ -21,6 +21,9 @@ kernel.sysrq = 16 + # Append the PID to the core filename + kernel.core_uses_pid = 1 + ++# https://bugzilla.redhat.com/show_bug.cgi?id=1689346 ++kernel.kptr_restrict = 1 ++ + # Source route verification + net.ipv4.conf.all.rp_filter = 1 + diff --git a/SOURCES/0291-cryptsetup-reduce-the-chance-that-we-will-be-OOM-kil.patch b/SOURCES/0291-cryptsetup-reduce-the-chance-that-we-will-be-OOM-kil.patch new file mode 100644 index 0000000..48f41cf --- /dev/null +++ b/SOURCES/0291-cryptsetup-reduce-the-chance-that-we-will-be-OOM-kil.patch @@ -0,0 +1,34 @@ +From 40612e4e7690c613cba7ac87b9d782724e623a39 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Wed, 27 Nov 2019 14:27:58 +0100 +Subject: [PATCH] cryptsetup: reduce the chance that we will be OOM killed + +cryptsetup introduced optional locking scheme that should serialize +unlocking keyslots which use memory hard key derivation +function (argon2). Using the serialization should prevent OOM situation +in early boot while unlocking encrypted volumes. + +(cherry picked from commit 408c81f62454684dfbff1c95ce3210d06f256e58) + +Resolves: #1696602 +--- + src/cryptsetup/cryptsetup.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 4e1b3eff19..9071126c2e 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -656,6 +656,12 @@ int main(int argc, char *argv[]) { + if (arg_discards) + flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; + ++#ifdef CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF ++ /* Try to decrease the risk of OOM event if memory hard key derivation function is in use */ ++ /* https://gitlab.com/cryptsetup/cryptsetup/issues/446/ */ ++ flags |= CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF; ++#endif ++ + if (arg_timeout == USEC_INFINITY) + until = 0; + else diff --git a/SOURCES/0292-core-job-fix-breakage-of-ordering-dependencies-by-sy.patch b/SOURCES/0292-core-job-fix-breakage-of-ordering-dependencies-by-sy.patch new file mode 100644 index 0000000..4bfb5ef --- /dev/null +++ b/SOURCES/0292-core-job-fix-breakage-of-ordering-dependencies-by-sy.patch @@ -0,0 +1,136 @@ +From cb084637ba1c8558f1538ce300c5520a6764dc76 Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Mon, 28 Oct 2019 19:35:24 +0900 +Subject: [PATCH] core, job: fix breakage of ordering dependencies by systemctl + reload command + +Currently, systemctl reload command breaks ordering dependencies if it's +executed when its target service unit is in activating state. + +For example, prepare A.service, B.service and C.target as follows: + + # systemctl cat A.service B.service C.target + # /etc/systemd/system/A.service + [Unit] + Description=A + + [Service] + Type=oneshot + ExecStart=/usr/bin/echo A1 + ExecStart=/usr/bin/sleep 60 + ExecStart=/usr/bin/echo A2 + ExecReload=/usr/bin/echo A reloaded + RemainAfterExit=yes + + # /etc/systemd/system/B.service + [Unit] + Description=B + After=A.service + + [Service] + Type=oneshot + ExecStart=/usr/bin/echo B + RemainAfterExit=yes + + # /etc/systemd/system/C.target + [Unit] + Description=C + Wants=A.service B.service + +Start them. + + # systemctl daemon-reload + # systemctl start C.target + +Then, we have: + + # LANG=C journalctl --no-pager -u A.service -u B.service -u C.target -b + -- Logs begin at Mon 2019-09-09 00:25:06 EDT, end at Thu 2019-10-24 22:28:47 EDT. -- + Oct 24 22:27:47 localhost.localdomain systemd[1]: Starting A... + Oct 24 22:27:47 localhost.localdomain systemd[1]: A.service: Child 967 belongs to A.service. + Oct 24 22:27:47 localhost.localdomain systemd[1]: A.service: Main process exited, code=exited, status=0/SUCCESS + Oct 24 22:27:47 localhost.localdomain systemd[1]: A.service: Running next main command for state start. + Oct 24 22:27:47 localhost.localdomain systemd[1]: A.service: Passing 0 fds to service + Oct 24 22:27:47 localhost.localdomain systemd[1]: A.service: About to execute: /usr/bin/sleep 60 + Oct 24 22:27:47 localhost.localdomain systemd[1]: A.service: Forked /usr/bin/sleep as 968 + Oct 24 22:27:47 localhost.localdomain systemd[968]: A.service: Executing: /usr/bin/sleep 60 + Oct 24 22:27:52 localhost.localdomain systemd[1]: A.service: Trying to enqueue job A.service/reload/replace + Oct 24 22:27:52 localhost.localdomain systemd[1]: A.service: Merged into running job, re-running: A.service/reload as 1288 + Oct 24 22:27:52 localhost.localdomain systemd[1]: A.service: Enqueued job A.service/reload as 1288 + Oct 24 22:27:52 localhost.localdomain systemd[1]: A.service: Unit cannot be reloaded because it is inactive. + Oct 24 22:27:52 localhost.localdomain systemd[1]: A.service: Job 1288 A.service/reload finished, result=invalid + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Passing 0 fds to service + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: About to execute: /usr/bin/echo B + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Forked /usr/bin/echo as 970 + Oct 24 22:27:52 localhost.localdomain systemd[970]: B.service: Executing: /usr/bin/echo B + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Failed to send unit change signal for B.service: Connection reset by peer + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Changed dead -> start + Oct 24 22:27:52 localhost.localdomain systemd[1]: Starting B... + Oct 24 22:27:52 localhost.localdomain echo[970]: B + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Child 970 belongs to B.service. + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Main process exited, code=exited, status=0/SUCCESS + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Changed start -> exited + Oct 24 22:27:52 localhost.localdomain systemd[1]: B.service: Job 1371 B.service/start finished, result=done + Oct 24 22:27:52 localhost.localdomain systemd[1]: Started B. + Oct 24 22:27:52 localhost.localdomain systemd[1]: C.target: Job 1287 C.target/start finished, result=done + Oct 24 22:27:52 localhost.localdomain systemd[1]: Reached target C. + Oct 24 22:27:52 localhost.localdomain systemd[1]: C.target: Failed to send unit change signal for C.target: Connection reset by peer + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Child 968 belongs to A.service. + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Main process exited, code=exited, status=0/SUCCESS + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Running next main command for state start. + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Passing 0 fds to service + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: About to execute: /usr/bin/echo A2 + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Forked /usr/bin/echo as 972 + Oct 24 22:28:47 localhost.localdomain systemd[972]: A.service: Executing: /usr/bin/echo A2 + Oct 24 22:28:47 localhost.localdomain echo[972]: A2 + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Child 972 belongs to A.service. + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Main process exited, code=exited, status=0/SUCCESS + Oct 24 22:28:47 localhost.localdomain systemd[1]: A.service: Changed start -> exited + +The issue occurs not only in reload command, i.e.: + + - reload + - try-restart + - reload-or-restart + - reload-or-try-restart commands + +The cause of this issue is that job_type_collapse() doesn't take care of the +activating state. + +Fixes: #10464 +(cherry picked from commit d1559793df555212271e490a4a72f55826caf5b4) + +Resolves: #1766417 +--- + src/core/job.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/core/job.c b/src/core/job.c +index 8552ffb704..769ed6d603 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -403,21 +403,21 @@ JobType job_type_collapse(JobType t, Unit *u) { + + case JOB_TRY_RESTART: + s = unit_active_state(u); +- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) ++ if (!UNIT_IS_ACTIVE_OR_RELOADING(s)) + return JOB_NOP; + + return JOB_RESTART; + + case JOB_TRY_RELOAD: + s = unit_active_state(u); +- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) ++ if (!UNIT_IS_ACTIVE_OR_RELOADING(s)) + return JOB_NOP; + + return JOB_RELOAD; + + case JOB_RELOAD_OR_START: + s = unit_active_state(u); +- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s)) ++ if (!UNIT_IS_ACTIVE_OR_RELOADING(s)) + return JOB_START; + + return JOB_RELOAD; diff --git a/SOURCES/0293-debug-generator-enable-custom-systemd.debug_shell-tt.patch b/SOURCES/0293-debug-generator-enable-custom-systemd.debug_shell-tt.patch new file mode 100644 index 0000000..fb40563 --- /dev/null +++ b/SOURCES/0293-debug-generator-enable-custom-systemd.debug_shell-tt.patch @@ -0,0 +1,149 @@ +From 613a02b7d67864af1860e9137e2ee101d603463e Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Thu, 25 Apr 2019 12:19:16 +0200 +Subject: [PATCH] debug-generator: enable custom systemd.debug_shell tty + +(cherry picked from commit 93912e872fb14e9c372e090409e429084a6450f5) + +Resolves: #1723722 +--- + man/custom-entities.ent.in | 1 + + man/systemd-debug-generator.xml | 13 ++++++--- + meson.build | 1 + + src/debug-generator/debug-generator.c | 41 +++++++++++++++++++++------ + 4 files changed, 43 insertions(+), 13 deletions(-) + +diff --git a/man/custom-entities.ent.in b/man/custom-entities.ent.in +index e2bd44e5e7..85805777a0 100644 +--- a/man/custom-entities.ent.in ++++ b/man/custom-entities.ent.in +@@ -8,3 +8,4 @@ + + + ++ +diff --git a/man/systemd-debug-generator.xml b/man/systemd-debug-generator.xml +index fa88e8ac01..25d8b1a873 100644 +--- a/man/systemd-debug-generator.xml ++++ b/man/systemd-debug-generator.xml +@@ -1,6 +1,10 @@ + + +- ++ ++%entities; ++]> + +@@ -57,9 +61,10 @@ + option is + specified, the debug shell service + debug-shell.service is pulled into the boot +- transaction. It will spawn a debug shell on tty9 during early +- system startup. Note that the shell may also be turned on +- persistently by enabling it with ++ transaction and a debug shell will be spawned during early boot. ++ By default, &DEBUGTTY; is used, but a specific tty can also be set, ++ either with or without the /dev/ prefix. ++ Note that the shell may also be turned on persistently by enabling it with + systemctl1's + enable command. + is honored only by initial +diff --git a/meson.build b/meson.build +index fe82ca4ac2..70811c29cf 100644 +--- a/meson.build ++++ b/meson.build +@@ -768,6 +768,7 @@ conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) + + substs.set('SUSHELL', get_option('debug-shell')) + substs.set('DEBUGTTY', get_option('debug-tty')) ++conf.set_quoted('DEBUGTTY', get_option('debug-tty')) + + enable_debug_hashmap = false + enable_debug_mmap_cache = false +diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c +index 800d31cebe..ddef385833 100644 +--- a/src/debug-generator/debug-generator.c ++++ b/src/debug-generator/debug-generator.c +@@ -1,8 +1,11 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + + #include "alloc-util.h" ++#include "dropin.h" ++#include "generator.h" + #include "mkdir.h" + #include "parse-util.h" ++#include "path-util.h" + #include "proc-cmdline.h" + #include "special.h" + #include "string-util.h" +@@ -14,7 +17,7 @@ static char *arg_default_unit = NULL; + static const char *arg_dest = "/tmp"; + static char **arg_mask = NULL; + static char **arg_wants = NULL; +-static bool arg_debug_shell = false; ++static char *arg_debug_shell = NULL; + + static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { + int r; +@@ -50,15 +53,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + return log_oom(); + + } else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) { ++ const char *t = NULL; + +- if (value) { +- r = parse_boolean(value); +- if (r < 0) +- log_error("Failed to parse systemd.debug_shell= argument '%s', ignoring.", value); +- else +- arg_debug_shell = r; +- } else +- arg_debug_shell = true; ++ r = value ? parse_boolean(value) : 1; ++ if (r < 0) ++ t = skip_dev_prefix(value); ++ else if (r > 0) ++ t = skip_dev_prefix(DEBUGTTY); ++ ++ if (free_and_strdup(&arg_debug_shell, t) < 0) ++ return log_oom(); + + } else if (streq(key, "systemd.unit")) { + +@@ -136,6 +140,23 @@ static int generate_wants_symlinks(void) { + return r; + } + ++static void install_debug_shell_dropin(const char *dir) { ++ int r; ++ ++ if (streq(arg_debug_shell, skip_dev_prefix(DEBUGTTY))) ++ return; ++ ++ r = write_drop_in_format(dir, "debug-shell.service", 50, "tty", ++ "[Unit]\n" ++ "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n" ++ "ConditionPathExists=\n" ++ "[Service]\n" ++ "TTYPath=/dev/%s", ++ arg_debug_shell, arg_debug_shell); ++ if (r < 0) ++ log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m"); ++} ++ + int main(int argc, char *argv[]) { + int r, q; + +@@ -164,6 +185,8 @@ int main(int argc, char *argv[]) { + r = log_oom(); + goto finish; + } ++ ++ install_debug_shell_dropin(arg_dest); + } + + r = generate_mask_symlinks(); diff --git a/SOURCES/0294-test-cpu-set-util-fix-comparison-for-allocation-size.patch b/SOURCES/0294-test-cpu-set-util-fix-comparison-for-allocation-size.patch new file mode 100644 index 0000000..490a14f --- /dev/null +++ b/SOURCES/0294-test-cpu-set-util-fix-comparison-for-allocation-size.patch @@ -0,0 +1,118 @@ +From f4344bb4055cab8dc3bbe82a7f3d97fc6fabcb7e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 4 Jun 2019 09:19:04 +0200 +Subject: [PATCH] test-cpu-set-util: fix comparison for allocation size + +On i386, __cpu_mask is 4 bytes, so we'd check if c.allocated >= 0, and +gcc would warn about a bogus comparison. Let's round up. + +Fixes #12726. + +(cherry picked from commit a299ce058b41b21c87f36e77e2c563b0ddd1be0d) + +Related: #1734787 +--- + src/test/test-cpu-set-util.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 9522582891..3456add989 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -17,7 +17,7 @@ static void test_parse_cpu_set(void) { + /* Single value */ + assert_se(parse_cpu_set_full("0", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.set); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_ISSET_S(0, c.allocated, c.set)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 1); + +@@ -33,7 +33,7 @@ static void test_parse_cpu_set(void) { + /* Simple range (from CPUAffinity example) */ + assert_se(parse_cpu_set_full("1 2 4", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); + assert_se(c.set); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_ISSET_S(1, c.allocated, c.set)); + assert_se(CPU_ISSET_S(2, c.allocated, c.set)); + assert_se(CPU_ISSET_S(4, c.allocated, c.set)); +@@ -50,7 +50,7 @@ static void test_parse_cpu_set(void) { + + /* A more interesting range */ + assert_se(parse_cpu_set_full("0 1 2 3 8 9 10 11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -68,7 +68,7 @@ static void test_parse_cpu_set(void) { + + /* Quoted strings */ + assert_se(parse_cpu_set_full("8 '9' 10 \"11\"", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 4); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -83,7 +83,7 @@ static void test_parse_cpu_set(void) { + + /* Use commas as separators */ + assert_se(parse_cpu_set_full("0,1,2,3 8,9,10,11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -96,7 +96,7 @@ static void test_parse_cpu_set(void) { + + /* Commas with spaces (and trailing comma, space) */ + assert_se(parse_cpu_set_full("0, 1, 2, 3, 4, 5, 6, 7, 63, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 9); + for (cpu = 0; cpu < 8; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -113,7 +113,7 @@ static void test_parse_cpu_set(void) { + + /* Ranges */ + assert_se(parse_cpu_set_full("0-3,8-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -126,7 +126,7 @@ static void test_parse_cpu_set(void) { + + /* Ranges with trailing comma, space */ + assert_se(parse_cpu_set_full("0-3 8-11, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -143,13 +143,13 @@ static void test_parse_cpu_set(void) { + + /* Negative range (returns empty cpu_set) */ + assert_se(parse_cpu_set_full("3-0", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 0); + cpu_set_reset(&c); + + /* Overlapping ranges */ + assert_se(parse_cpu_set_full("0-7 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 12); + for (cpu = 0; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, c.allocated, c.set)); +@@ -164,7 +164,7 @@ static void test_parse_cpu_set(void) { + + /* Mix ranges and individual CPUs */ + assert_se(parse_cpu_set_full("0,2 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0); +- assert_se(c.allocated >= sizeof(__cpu_mask) / 8); ++ assert_se(c.allocated >= DIV_ROUND_UP(sizeof(__cpu_mask), 8)); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 10); + assert_se(CPU_ISSET_S(0, c.allocated, c.set)); + assert_se(CPU_ISSET_S(2, c.allocated, c.set)); diff --git a/SOURCES/0295-test-cpu-set-util-fix-allocation-size-check-on-i386.patch b/SOURCES/0295-test-cpu-set-util-fix-allocation-size-check-on-i386.patch new file mode 100644 index 0000000..cbb8483 --- /dev/null +++ b/SOURCES/0295-test-cpu-set-util-fix-allocation-size-check-on-i386.patch @@ -0,0 +1,30 @@ +From 3cf9361996b796eae0bda12aec8c92ddae1d5d48 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 4 Jun 2019 09:40:38 +0200 +Subject: [PATCH] test-cpu-set-util: fix allocation size check on i386 + +We get just 28 bytes not 32 as on 64-bit architectures (__cpu_set_t is 4 bytes, +we need at least 26, so 28 satisfies the constraints). + +(cherry picked from commit 64412970ac0d4b6f5c4bbd8816edc9bff9eab2de) + +Related: #1734787 +--- + src/test/test-cpu-set-util.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 3456add989..136eaca82d 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -256,7 +256,9 @@ static void test_cpu_set_to_from_dbus(void) { + assert_se(array); + assert_se(allocated == c.allocated); + +- assert(memcmp(array, expected, sizeof expected) == 0); ++ assert_se(allocated <= sizeof expected); ++ assert_se(allocated >= DIV_ROUND_UP(201u, 8u)); /* We need at least 201 bits for our mask */ ++ assert(memcmp(array, expected, allocated) == 0); + + assert_se(cpu_set_from_dbus(array, allocated, &c2) == 0); + assert_se(c2.set); diff --git a/SOURCES/0296-catalog-fix-name-of-variable.patch b/SOURCES/0296-catalog-fix-name-of-variable.patch new file mode 100644 index 0000000..1e752eb --- /dev/null +++ b/SOURCES/0296-catalog-fix-name-of-variable.patch @@ -0,0 +1,531 @@ +From c3513c7bcc27210c89edad1740e1190e693df86f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 15 Oct 2018 22:41:49 +0200 +Subject: [PATCH] catalog: fix name of variable + +All the messages would (literally) say "The start-up result is RESULT." +because @RESULT@ was not defined. + +Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1639482 +and the first part of #8005. + +Fixup for 646cc98dc81c4d0edbc1b57e7bca0f474b47e270. + +(cherry picked from commit 65d51875c2a7b27b61de090f1bd6311b0cef2016) + +Resolves: #1677768 +--- + catalog/systemd.be.catalog.in | 6 +++--- + catalog/systemd.be@latin.catalog.in | 6 +++--- + catalog/systemd.bg.catalog.in | 6 +++--- + catalog/systemd.catalog.in | 6 +++--- + catalog/systemd.da.catalog.in | 6 +++--- + catalog/systemd.fr.catalog.in | 6 +++--- + catalog/systemd.hr.catalog.in | 6 +++--- + catalog/systemd.hu.catalog.in | 6 +++--- + catalog/systemd.it.catalog.in | 6 +++--- + catalog/systemd.ko.catalog.in | 6 +++--- + catalog/systemd.pl.catalog.in | 6 +++--- + catalog/systemd.pt_BR.catalog.in | 6 +++--- + catalog/systemd.ru.catalog.in | 6 +++--- + catalog/systemd.sr.catalog.in | 6 +++--- + catalog/systemd.zh_CN.catalog.in | 6 +++--- + catalog/systemd.zh_TW.catalog.in | 6 +++--- + 16 files changed, 48 insertions(+), 48 deletions(-) + +diff --git a/catalog/systemd.be.catalog.in b/catalog/systemd.be.catalog.in +index 5011ea268d..2c59898683 100644 +--- a/catalog/systemd.be.catalog.in ++++ b/catalog/systemd.be.catalog.in +@@ -175,7 +175,7 @@ Support: %SUPPORT_URL% + + Працэс запуску юніта @UNIT@ завершаны. + +-Вынік: @RESULT@. ++Вынік: @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Юніт @UNIT@ спыняецца +@@ -198,7 +198,7 @@ Support: %SUPPORT_URL% + + Збой юніта @UNIT@. + +-Вынік: @RESULT@. ++Вынік: @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Юніт @UNIT@ перачытвае сваю канфігурацыю +@@ -214,7 +214,7 @@ Support: %SUPPORT_URL% + + Юніт @UNIT@ перачытаў сваю канфігурацыю. + +-Вынік: @RESULT@. ++Вынік: @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Працэс @EXECUTABLE@ не можа быць выкананы +diff --git a/catalog/systemd.be@latin.catalog.in b/catalog/systemd.be@latin.catalog.in +index 6a8b092669..1d024fea12 100644 +--- a/catalog/systemd.be@latin.catalog.in ++++ b/catalog/systemd.be@latin.catalog.in +@@ -178,7 +178,7 @@ Support: %SUPPORT_URL% + + Praces zapusku junita @UNIT@ zavieršany. + +-Vynik: @RESULT@. ++Vynik: @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Junit @UNIT@ spyniajecca +@@ -201,7 +201,7 @@ Support: %SUPPORT_URL% + + Zboj junita @UNIT@. + +-Vynik: @RESULT@. ++Vynik: @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Junit @UNIT@ pieračytvaje svaju kanfihuracyju +@@ -217,7 +217,7 @@ Support: %SUPPORT_URL% + + Junit @UNIT@ pieračytaŭ svaju kanfihuracyju. + +-Vynik: @RESULT@. ++Vynik: @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Praces @EXECUTABLE@ nie moža być vykanany +diff --git a/catalog/systemd.bg.catalog.in b/catalog/systemd.bg.catalog.in +index 64d616f381..41f7b21bce 100644 +--- a/catalog/systemd.bg.catalog.in ++++ b/catalog/systemd.bg.catalog.in +@@ -178,7 +178,7 @@ Support: %SUPPORT_URL% + + Стартирането на модул „@UNIT@“ завърши. + +-Резултатът е: @RESULT@ ++Резултатът е: @JOB_RESULT@ + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Модул „@UNIT@“ се спира +@@ -201,7 +201,7 @@ Support: %SUPPORT_URL% + + Модулът „@UNIT@“ не успя да стартира. + +-Резултатът е: @RESULT@ ++Резултатът е: @JOB_RESULT@ + + -- d34d037fff1847e6ae669a370e694725 + Subject: Модулът „@UNIT@“ започна презареждане на настройките си +@@ -217,7 +217,7 @@ Support: %SUPPORT_URL% + + Модулът „@UNIT@“ завърши презареждането на настройките си. + +-Резултатът e: @RESULT@ ++Резултатът e: @JOB_RESULT@ + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Програмата „@EXECUTABLE@“ не успя да се стартира +diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in +index 8234e387cf..49a45890f6 100644 +--- a/catalog/systemd.catalog.in ++++ b/catalog/systemd.catalog.in +@@ -202,7 +202,7 @@ Support: %SUPPORT_URL% + + Unit @UNIT@ has finished starting up. + +-The start-up result is @RESULT@. ++The start-up result is @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Unit @UNIT@ has begun shutting down +@@ -225,7 +225,7 @@ Support: %SUPPORT_URL% + + Unit @UNIT@ has failed. + +-The result is @RESULT@. ++The result is @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Unit @UNIT@ has begun reloading its configuration +@@ -241,7 +241,7 @@ Support: %SUPPORT_URL% + + Unit @UNIT@ has finished reloading its configuration + +-The result is @RESULT@. ++The result is @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Process @EXECUTABLE@ could not be executed +diff --git a/catalog/systemd.da.catalog.in b/catalog/systemd.da.catalog.in +index 4e2bec8a0f..aecfafa05f 100644 +--- a/catalog/systemd.da.catalog.in ++++ b/catalog/systemd.da.catalog.in +@@ -159,7 +159,7 @@ Support: %SUPPORT_URL% + + Enhed @UNIT@ er færdig med at starte op. + +-Resultat for opstart er @RESULT@. ++Resultat for opstart er @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Enhed @UNIT@ har påbegyndt nedlukning +@@ -182,7 +182,7 @@ Support: %SUPPORT_URL% + + Enhed @UNIT@ har fejlet. + +-Resultatet er @RESULT@ ++Resultatet er @JOB_RESULT@ + + -- d34d037fff1847e6ae669a370e694725 + Subject: Enhed @UNIT@ har påbegyndt genindlæsning af sin konfiguration +@@ -198,7 +198,7 @@ Support: %SUPPORT_URL% + + Enhed @UNIT@ er færdig med at genindlæse sin konfiguration + +-Resultatet er: @RESULT@. ++Resultatet er: @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Process @EXECUTABLE@ kunne ikke eksekveres +diff --git a/catalog/systemd.fr.catalog.in b/catalog/systemd.fr.catalog.in +index 156b1a37dc..13edd083cb 100644 +--- a/catalog/systemd.fr.catalog.in ++++ b/catalog/systemd.fr.catalog.in +@@ -191,7 +191,7 @@ Subject: L'unité (unit) @UNIT@ a terminé son démarrage + Defined-By: systemd + Support: %SUPPORT_URL% + +-L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @RESULT@. ++L'unité (unit) @UNIT@ a terminé son démarrage, avec le résultat @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: L'unité (unit) @UNIT@ a commencé à s'arrêter +@@ -212,7 +212,7 @@ Subject: L'unité (unit) @UNIT@ a échoué + Defined-By: systemd + Support: %SUPPORT_URL% + +-L'unité (unit) @UNIT@ a échoué, avec le résultat @RESULT@. ++L'unité (unit) @UNIT@ a échoué, avec le résultat @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: L'unité (unit) @UNIT@ a commencé à recharger sa configuration +@@ -227,7 +227,7 @@ Defined-By: systemd + Support: %SUPPORT_URL% + + L'unité (unit) @UNIT@ a terminé de recharger configuration, +-avec le résultat @RESULT@. ++avec le résultat @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Le processus @EXECUTABLE@ n'a pas pu être exécuté +diff --git a/catalog/systemd.hr.catalog.in b/catalog/systemd.hr.catalog.in +index c4808b4c7d..4526ae2a8c 100644 +--- a/catalog/systemd.hr.catalog.in ++++ b/catalog/systemd.hr.catalog.in +@@ -173,7 +173,7 @@ Support: %SUPPORT_URL% + + Jedinica @UNIT@ je završila pokretanje. + +-Rezultat pokretanja je @RESULT@. ++Rezultat pokretanja je @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Jedinica @UNIT@ je započela isključivanje +@@ -196,7 +196,7 @@ Support: %SUPPORT_URL% + + Jedinica @UNIT@ nije uspjela. + +-Rezultat je @RESULT@. ++Rezultat je @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Jedinica @UNIT@ je započela ponovno učitavati podešavanja +@@ -212,7 +212,7 @@ Support: %SUPPORT_URL% + + Jedinica @UNIT@ je završila ponovno učitavati podešavanja + +-Rezultat je @RESULT@. ++Rezultat je @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Proces @EXECUTABLE@ se ne može pokrenuti +diff --git a/catalog/systemd.hu.catalog.in b/catalog/systemd.hu.catalog.in +index 6c6d7e7934..5565b80b2a 100644 +--- a/catalog/systemd.hu.catalog.in ++++ b/catalog/systemd.hu.catalog.in +@@ -161,7 +161,7 @@ Support: %SUPPORT_URL% + + A(z) @UNIT@ egység befejezte az indulást + +-Az indítás eredménye: @RESULT@. ++Az indítás eredménye: @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: A(z) @UNIT@ egység megkezdte a leállást +@@ -184,7 +184,7 @@ Support: %SUPPORT_URL% + + A(z) @UNIT@ egység hibát jelzett. + +-Az eredmény: @RESULT@. ++Az eredmény: @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: A(z) @UNIT@ egység megkezdte a beállításainak újratöltését +@@ -200,7 +200,7 @@ Support: %SUPPORT_URL% + + A(z) @UNIT@ egység befejezte a beállításainak újratöltését. + +-Az eredmény: @RESULT@. ++Az eredmény: @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: A folyamat végrehajtása sikertelen: @EXECUTABLE@ +diff --git a/catalog/systemd.it.catalog.in b/catalog/systemd.it.catalog.in +index 4fd1f2a933..8ce4fa5d92 100644 +--- a/catalog/systemd.it.catalog.in ++++ b/catalog/systemd.it.catalog.in +@@ -191,7 +191,7 @@ Support: %SUPPORT_URL% + + L'unità @UNIT@ ha terminato la fase di avvio. + +-La fase di avvio è @RESULT@. ++La fase di avvio è @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: L'unità @UNIT@ inizia la fase di spegnimento +@@ -214,7 +214,7 @@ Support: %SUPPORT_URL% + + L'unità @UNIT@ è fallita. + +-Il risultato è @RESULT@. ++Il risultato è @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: L'unità @UNIT@ inizia a caricare la propria configurazione +@@ -230,7 +230,7 @@ Support: %SUPPORT_URL% + + L'unità @UNIT@ è terminata ricaricando la propria configurazione + +-Il risultato è @RESULT@. ++Il risultato è @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Il processo @EXECUTABLE@ non può essere eseguito +diff --git a/catalog/systemd.ko.catalog.in b/catalog/systemd.ko.catalog.in +index fc0faad02c..59fbde8b62 100644 +--- a/catalog/systemd.ko.catalog.in ++++ b/catalog/systemd.ko.catalog.in +@@ -182,7 +182,7 @@ Support: %SUPPORT_URL% + + @UNIT@ 유닛 시동을 마쳤습니다. + +-시동 결과는 @RESULT@ 입니다. ++시동 결과는 @JOB_RESULT@ 입니다. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: @UNIT@ 유닛 끝내기 동작 시작 +@@ -205,7 +205,7 @@ Support: %SUPPORT_URL% + + @UNIT@ 유닛 동작에 실패했습니다. + +-결과는 @RESULT@ 입니다. ++결과는 @JOB_RESULT@ 입니다. + + -- d34d037fff1847e6ae669a370e694725 + Subject: @UNIT@ 유닛 설정 다시 읽기 시작 +@@ -221,7 +221,7 @@ Support: %SUPPORT_URL% + + @UNIT@ 유닛의 설정 다시 읽기 동작을 끝냈습니다. + +-결과는 @RESULT@ 입니다. ++결과는 @JOB_RESULT@ 입니다. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: @EXECUTABLE@ 프로세스 시작할 수 없음 +diff --git a/catalog/systemd.pl.catalog.in b/catalog/systemd.pl.catalog.in +index 998894bd0a..b73f56ca11 100644 +--- a/catalog/systemd.pl.catalog.in ++++ b/catalog/systemd.pl.catalog.in +@@ -201,7 +201,7 @@ Support: %SUPPORT_URL% + + Jednostka @UNIT@ ukończyła uruchamianie. + +-Wynik uruchamiania: @RESULT@. ++Wynik uruchamiania: @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Rozpoczęto wyłączanie jednostki @UNIT@ +@@ -224,7 +224,7 @@ Support: %SUPPORT_URL% + + Jednostka @UNIT@ się nie powiodła. + +-Wynik: @RESULT@. ++Wynik: @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Rozpoczęto ponowne wczytywanie konfiguracji jednostki @UNIT@ +@@ -240,7 +240,7 @@ Support: %SUPPORT_URL% + + Jednostka @UNIT@ ukończyła ponowne wczytywanie swojej konfiguracji. + +-Wynik: @RESULT@. ++Wynik: @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Nie można wykonać procesu @EXECUTABLE@ +diff --git a/catalog/systemd.pt_BR.catalog.in b/catalog/systemd.pt_BR.catalog.in +index db1cb03198..edaefb7164 100644 +--- a/catalog/systemd.pt_BR.catalog.in ++++ b/catalog/systemd.pt_BR.catalog.in +@@ -162,7 +162,7 @@ Support: %SUPPORT_URL% + + A unidade @UNIT@ concluiu a inicialização. + +-The start-up result is @RESULT@. ++The start-up result is @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Unidade @UNIT@ sendo desligado +@@ -185,7 +185,7 @@ Support: %SUPPORT_URL% + + A unidade @UNIT@ falhou. + +-O resultado é @RESULT@. ++O resultado é @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Unidade @UNIT@ iniciou recarregamento de sua configuração +@@ -201,7 +201,7 @@ Support: %SUPPORT_URL% + + A unidade @UNIT@ concluiu o recarregamento de sua configuração. + +-O resultado é @RESULT@. ++O resultado é @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Processo @EXECUTABLE@ não pôde ser executado +diff --git a/catalog/systemd.ru.catalog.in b/catalog/systemd.ru.catalog.in +index 645edaa922..ccdc685037 100644 +--- a/catalog/systemd.ru.catalog.in ++++ b/catalog/systemd.ru.catalog.in +@@ -227,7 +227,7 @@ Support: %SUPPORT_URL% + + Процесс запуска юнита @UNIT@ был завершен. + +-Результат: @RESULT@. ++Результат: @JOB_RESULT@. + + # Subject: Unit @UNIT@ has begun shutting down + -- de5b426a63be47a7b6ac3eaac82e2f6f +@@ -253,7 +253,7 @@ Support: %SUPPORT_URL% + + Произошел сбой юнита @UNIT@. + +-Результат: @RESULT@. ++Результат: @JOB_RESULT@. + + # Subject: Unit @UNIT@ has begun with reloading its configuration + -- d34d037fff1847e6ae669a370e694725 +@@ -271,7 +271,7 @@ Support: %SUPPORT_URL% + + Юнит @UNIT@ завершил процесс перечитывания своей конфигурации. + +-Результат: @RESULT@. ++Результат: @JOB_RESULT@. + + # Subject: Process @EXECUTABLE@ could not be executed + -- 641257651c1b4ec9a8624d7a40a9e1e7 +diff --git a/catalog/systemd.sr.catalog.in b/catalog/systemd.sr.catalog.in +index f5746715a4..7cb6546d43 100644 +--- a/catalog/systemd.sr.catalog.in ++++ b/catalog/systemd.sr.catalog.in +@@ -158,7 +158,7 @@ Support: %SUPPORT_URL% + + Јединица @UNIT@ је завршила са покретањем. + +-Исход покретања је @RESULT@. ++Исход покретања је @JOB_RESULT@. + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: Јединица @UNIT@ је почела са гашењем +@@ -181,7 +181,7 @@ Support: %SUPPORT_URL% + + Јединица @UNIT@ је пукла. + +-Исход је @RESULT@. ++Исход је @JOB_RESULT@. + + -- d34d037fff1847e6ae669a370e694725 + Subject: Јединица @UNIT@ је почела са поновним учитавањем свог подешавања +@@ -197,7 +197,7 @@ Support: %SUPPORT_URL% + + Јединица @UNIT@ је завршила са поновним учитавањем свог подешавања + +-Исход је @RESULT@. ++Исход је @JOB_RESULT@. + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: Процес @EXECUTABLE@ није могао бити извршен +diff --git a/catalog/systemd.zh_CN.catalog.in b/catalog/systemd.zh_CN.catalog.in +index fa58448acf..d6ac2592b8 100644 +--- a/catalog/systemd.zh_CN.catalog.in ++++ b/catalog/systemd.zh_CN.catalog.in +@@ -156,7 +156,7 @@ Support: %SUPPORT_URL% + + @UNIT@ 单元已结束启动。 + +-启动结果为“@RESULT@”。 ++启动结果为“@JOB_RESULT@”。 + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: @UNIT@ 单元已开始停止操作 +@@ -179,7 +179,7 @@ Support: %SUPPORT_URL% + + @UNIT@ 单元已失败。 + +-结果为“@RESULT@”。 ++结果为“@JOB_RESULT@”。 + + -- d34d037fff1847e6ae669a370e694725 + Subject: @UNIT@ 单元已开始重新载入其配置 +@@ -195,7 +195,7 @@ Support: %SUPPORT_URL% + + @UNIT@ 单元已结束配置重载入操作。 + +-结果为“@RESULT@”。 ++结果为“@JOB_RESULT@”。 + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: 进程 @EXECUTABLE@ 无法执行 +diff --git a/catalog/systemd.zh_TW.catalog.in b/catalog/systemd.zh_TW.catalog.in +index 17bd2bc9af..a468c2f6bf 100644 +--- a/catalog/systemd.zh_TW.catalog.in ++++ b/catalog/systemd.zh_TW.catalog.in +@@ -160,7 +160,7 @@ Support: %SUPPORT_URL% + + 單位 @UNIT@ 啟動已結束。 + +-啟動結果為 @RESULT@。 ++啟動結果為 @JOB_RESULT@。 + + -- de5b426a63be47a7b6ac3eaac82e2f6f + Subject: 單位 @UNIT@ 已開始關閉 +@@ -183,7 +183,7 @@ Support: %SUPPORT_URL% + + 單位 @UNIT@ 已失敗。 + +-結果為 @RESULT@。 ++結果為 @JOB_RESULT@。 + + -- d34d037fff1847e6ae669a370e694725 + Subject: 單位 @UNIT@ 已開始重新載入其設定 +@@ -199,7 +199,7 @@ Support: %SUPPORT_URL% + + 單位 @UNIT@ 已結束重新載入其設定 + +-結果為 @RESULT@。 ++結果為 @JOB_RESULT@。 + + -- 641257651c1b4ec9a8624d7a40a9e1e7 + Subject: 行程 @EXECUTABLE@ 無法執行 diff --git a/SOURCES/0297-cryptsetup-add-keyfile-timeout-to-allow-a-keydev-tim.patch b/SOURCES/0297-cryptsetup-add-keyfile-timeout-to-allow-a-keydev-tim.patch new file mode 100644 index 0000000..8e99e2f --- /dev/null +++ b/SOURCES/0297-cryptsetup-add-keyfile-timeout-to-allow-a-keydev-tim.patch @@ -0,0 +1,273 @@ +From 0f7a4f49a7ce95e87061afe03ac40662a1eb0e2d Mon Sep 17 00:00:00 2001 +From: shinygold <10763595+shinygold@users.noreply.github.com> +Date: Tue, 16 Jul 2019 13:06:16 +0200 +Subject: [PATCH] cryptsetup: add keyfile-timeout to allow a keydev timeout and + allow to fallback to a password if it fails. + +(cherry picked from commit 50d2eba27b9bfc77ef6b40e5721713846815418b) + +Resolves: #1763155 +--- + src/cryptsetup/cryptsetup-generator.c | 119 ++++++++++++++++++-------- + src/cryptsetup/cryptsetup.c | 5 +- + 2 files changed, 89 insertions(+), 35 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index 52c1262728..1e8e3ba00d 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -40,10 +40,39 @@ static Hashmap *arg_disks = NULL; + static char *arg_default_options = NULL; + static char *arg_default_keyfile = NULL; + +-static int generate_keydev_mount(const char *name, const char *keydev, char **unit, char **mount) { +- _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL; ++static int split_keyspec(const char *keyspec, char **keyfile, char **keydev) { ++ _cleanup_free_ char *kfile = NULL, *kdev = NULL; ++ char *c; ++ ++ assert(keyspec); ++ assert(keyfile); ++ assert(keydev); ++ ++ c = strrchr(keyspec, ':'); ++ if (c) { ++ kfile = strndup(keyspec, c-keyspec); ++ kdev = strdup(c + 1); ++ if (!*kfile || !*kdev) ++ return log_oom(); ++ } else { ++ /* No keydev specified */ ++ kfile = strdup(keyspec); ++ kdev = NULL; ++ if (!*kfile) ++ return log_oom(); ++ } ++ ++ *keyfile = TAKE_PTR(kfile); ++ *keydev = TAKE_PTR(kdev); ++ ++ return 0; ++} ++ ++static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) { ++ _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; ++ usec_t timeout_us; + + assert(name); + assert(keydev); +@@ -88,7 +117,25 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un + "[Mount]\n" + "What=%s\n" + "Where=%s\n" +- "Options=ro\n", what, where); ++ "Options=ro%s\n", what, where, canfail ? ",nofail" : ""); ++ ++ if (keydev_timeout) { ++ r = parse_sec_fix_0(keydev_timeout, &timeout_us); ++ if (r >= 0) { ++ r = unit_name_from_path(what, ".device", &device_unit); ++ if (r < 0) ++ return log_error_errno(r, "Failed to generate unit name: %m"); ++ ++ r = write_drop_in_format(arg_dest, device_unit, 90, "device-timeout", ++ "# Automatically generated by systemd-cryptsetup-generator \n\n" ++ "[Unit]\nJobRunningTimeoutSec=%s", keydev_timeout); ++ if (r < 0) ++ return log_error_errno(r, "Failed to write device drop-in: %m"); ++ ++ } else ++ log_warning_errno(r, "Failed to parse %s, ignoring: %m", keydev_timeout); ++ ++ } + + r = fflush_and_check(f); + if (r < 0) +@@ -103,16 +150,17 @@ static int generate_keydev_mount(const char *name, const char *keydev, char **un + static int create_disk( + const char *name, + const char *device, +- const char *keydev, + const char *password, ++ const char *keydev, + const char *options) { + + _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL, +- *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL, *keydev_mount = NULL; ++ *keydev_mount = NULL, *keyfile_timeout_value = NULL, *password_escaped = NULL, ++ *filtered = NULL, *u_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *dmname; + bool noauto, nofail, tmp, swap, netdev; +- int r; ++ int r, keyfile_can_timeout; + + assert(name); + assert(device); +@@ -123,6 +171,10 @@ static int create_disk( + swap = fstab_test_option(options, "swap\0"); + netdev = fstab_test_option(options, "_netdev\0"); + ++ keyfile_can_timeout = fstab_filter_options(options, "keyfile-timeout\0", NULL, &keyfile_timeout_value, NULL); ++ if (keyfile_can_timeout < 0) ++ return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m"); ++ + if (tmp && swap) { + log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name); + return -EINVAL; +@@ -152,12 +204,6 @@ static int create_disk( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- if (password) { +- password_escaped = specifier_escape(password); +- if (!password_escaped) +- return log_oom(); +- } +- + if (keydev && !password) { + log_error("Key device is specified, but path to the password file is missing."); + return -EINVAL; +@@ -178,10 +224,16 @@ static int create_disk( + "After=%s\n", + netdev ? "remote-fs-pre.target" : "cryptsetup-pre.target"); + ++ if (password) { ++ password_escaped = specifier_escape(password); ++ if (!password_escaped) ++ return log_oom(); ++ } ++ + if (keydev) { + _cleanup_free_ char *unit = NULL, *p = NULL; + +- r = generate_keydev_mount(name, keydev, &unit, &keydev_mount); ++ r = generate_keydev_mount(name, keydev, keyfile_timeout_value, keyfile_can_timeout > 0, &unit, &keydev_mount); + if (r < 0) + return log_error_errno(r, "Failed to generate keydev mount unit: %m"); + +@@ -190,6 +242,12 @@ static int create_disk( + return log_oom(); + + free_and_replace(password_escaped, p); ++ ++ fprintf(f, "After=%s\n", unit); ++ if (keyfile_can_timeout > 0) ++ fprintf(f, "Wants=%s\n", unit); ++ else ++ fprintf(f, "Requires=%s\n", unit); + } + + if (!nofail) +@@ -197,7 +255,7 @@ static int create_disk( + "Before=%s\n", + netdev ? "remote-cryptsetup.target" : "cryptsetup.target"); + +- if (password) { ++ if (password && !keydev) { + if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random")) + fputs("After=systemd-random-seed.service\n", f); + else if (!STR_IN_SET(password, "-", "none")) { +@@ -271,7 +329,7 @@ static int create_disk( + + if (keydev) + fprintf(f, +- "ExecStartPost=" UMOUNT_PATH " %s\n\n", ++ "ExecStartPost=-" UMOUNT_PATH " %s\n\n", + keydev_mount); + + r = fflush_and_check(f); +@@ -394,7 +452,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + } else if (streq(key, "luks.key")) { + size_t n; + _cleanup_free_ char *keyfile = NULL, *keydev = NULL; +- char *c; + const char *keyspec; + + if (proc_cmdline_value_missing(key, value)) +@@ -421,23 +478,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + return log_oom(); + + keyspec = value + n + 1; +- c = strrchr(keyspec, ':'); +- if (c) { +- *c = '\0'; +- keyfile = strdup(keyspec); +- keydev = strdup(c + 1); +- +- if (!keyfile || !keydev) +- return log_oom(); +- } else { +- /* No keydev specified */ +- keyfile = strdup(keyspec); +- if (!keyfile) +- return log_oom(); +- } ++ r = split_keyspec(keyspec, &keyfile, &keydev); ++ if (r < 0) ++ return r; + + free_and_replace(d->keyfile, keyfile); + free_and_replace(d->keydev, keydev); ++ + } else if (streq(key, "luks.name")) { + + if (proc_cmdline_value_missing(key, value)) +@@ -485,7 +532,7 @@ static int add_crypttab_devices(void) { + int r, k; + char line[LINE_MAX], *l, *uuid; + crypto_device *d = NULL; +- _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL; ++ _cleanup_free_ char *name = NULL, *device = NULL, *keydev = NULL, *keyfile = NULL, *keyspec = NULL, *options = NULL; + + if (!fgets(line, sizeof(line), f)) + break; +@@ -496,7 +543,7 @@ static int add_crypttab_devices(void) { + if (IN_SET(*l, 0, '#')) + continue; + +- k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options); ++ k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyspec, &options); + if (k < 2 || k > 4) { + log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line); + continue; +@@ -515,7 +562,11 @@ static int add_crypttab_devices(void) { + continue; + } + +- r = create_disk(name, device, NULL, keyfile, (d && d->options) ? d->options : options); ++ r = split_keyspec(keyspec, &keyfile, &keydev); ++ if (r < 0) ++ return r; ++ ++ r = create_disk(name, device, keyfile, keydev, (d && d->options) ? d->options : options); + if (r < 0) + return r; + +@@ -555,7 +606,7 @@ static int add_proc_cmdline_devices(void) { + else + options = "timeout=0"; + +- r = create_disk(d->name, device, d->keydev, d->keyfile ?: arg_default_keyfile, options); ++ r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, d->keydev, options); + if (r < 0) + return r; + } +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 9071126c2e..0881aea915 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -69,7 +69,10 @@ static int parse_one_option(const char *option) { + assert(option); + + /* Handled outside of this tool */ +- if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev")) ++ if (STR_IN_SET(option, "noauto", "auto", "nofail", "fail", "_netdev", "keyfile-timeout")) ++ return 0; ++ ++ if (startswith(option, "keyfile-timeout=")) + return 0; + + if ((val = startswith(option, "cipher="))) { diff --git a/SOURCES/0298-cryptsetup-add-documentation-for-keyfile-timeout.patch b/SOURCES/0298-cryptsetup-add-documentation-for-keyfile-timeout.patch new file mode 100644 index 0000000..c43658a --- /dev/null +++ b/SOURCES/0298-cryptsetup-add-documentation-for-keyfile-timeout.patch @@ -0,0 +1,44 @@ +From fdb86185b56619c59602c6546fd0710eec4a6e85 Mon Sep 17 00:00:00 2001 +From: shinygold <10763595+shinygold@users.noreply.github.com> +Date: Tue, 16 Jul 2019 13:05:34 +0200 +Subject: [PATCH] cryptsetup: add documentation for keyfile-timeout + +(cherry picked from commit 4e1334512debb27f4a0c4a6da237a4b8d59fea08) + +Related: #1763155 +--- + man/crypttab.xml | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/man/crypttab.xml b/man/crypttab.xml +index 3574ce00da..6074315980 100644 +--- a/man/crypttab.xml ++++ b/man/crypttab.xml +@@ -150,6 +150,17 @@ + sequential order. + + ++ ++ ++ ++ Specifies the timeout for the device on ++ which the key file resides and falls back to a password if ++ it could not be mounted. See ++ systemd-cryptsetup-generator8 ++ for key files on external devices. ++ ++ ++ + + + +@@ -417,7 +428,8 @@ + luks UUID=2505567a-9e27-4efe-a4d5-15ad146c258b + swap /dev/sda7 /dev/urandom swap + truecrypt /dev/sda2 /etc/container_password tcrypt +-hidden /mnt/tc_hidden /dev/null tcrypt-hidden,tcrypt-keyfile=/etc/keyfile ++hidden /mnt/tc_hidden /dev/null tcrypt-hidden,tcrypt-keyfile=/etc/keyfile ++external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s + + + diff --git a/SOURCES/0299-cryptsetup-use-unabbrieviated-variable-names.patch b/SOURCES/0299-cryptsetup-use-unabbrieviated-variable-names.patch new file mode 100644 index 0000000..be86edd --- /dev/null +++ b/SOURCES/0299-cryptsetup-use-unabbrieviated-variable-names.patch @@ -0,0 +1,63 @@ +From 0577d8378645c1ecd909b74403cefe31ed569398 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 1 Aug 2019 08:13:13 +0200 +Subject: [PATCH] cryptsetup: use unabbrieviated variable names + +Now that "ret_" has been added to the output variables, we can name +the internal variables without artificial abbrevs. + +(cherry picked from commit 5d2100dc4c32abbce4109e75cbfbbef6e1b2b7b1) + +Related: #1763155 +--- + src/cryptsetup/cryptsetup-generator.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index 1e8e3ba00d..7b234e37be 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -40,30 +40,30 @@ static Hashmap *arg_disks = NULL; + static char *arg_default_options = NULL; + static char *arg_default_keyfile = NULL; + +-static int split_keyspec(const char *keyspec, char **keyfile, char **keydev) { +- _cleanup_free_ char *kfile = NULL, *kdev = NULL; +- char *c; ++static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_keydev) { ++ _cleanup_free_ char *keyfile = NULL, *keydev = NULL; ++ const char *c; + + assert(keyspec); +- assert(keyfile); +- assert(keydev); ++ assert(ret_keyfile); ++ assert(ret_keydev); + + c = strrchr(keyspec, ':'); + if (c) { +- kfile = strndup(keyspec, c-keyspec); +- kdev = strdup(c + 1); +- if (!*kfile || !*kdev) ++ keyfile = strndup(keyspec, c-keyspec); ++ keydev = strdup(c + 1); ++ if (!keyfile || !keydev) + return log_oom(); + } else { + /* No keydev specified */ +- kfile = strdup(keyspec); +- kdev = NULL; +- if (!*kfile) ++ keyfile = strdup(keyspec); ++ keydev = NULL; ++ if (!keyfile) + return log_oom(); + } + +- *keyfile = TAKE_PTR(kfile); +- *keydev = TAKE_PTR(kdev); ++ *ret_keyfile = TAKE_PTR(keyfile); ++ *ret_keydev = TAKE_PTR(keydev); + + return 0; + } diff --git a/SOURCES/0300-cryptsetup-don-t-assert-on-variable-which-is-optiona.patch b/SOURCES/0300-cryptsetup-don-t-assert-on-variable-which-is-optiona.patch new file mode 100644 index 0000000..7154dca --- /dev/null +++ b/SOURCES/0300-cryptsetup-don-t-assert-on-variable-which-is-optiona.patch @@ -0,0 +1,37 @@ +From 5cdb2b0b2a0f8f89f97053b0633b8419506d4e28 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 1 Aug 2019 08:15:43 +0200 +Subject: [PATCH] cryptsetup: don't assert on variable which is optional + +https://github.com/systemd/systemd/commit/50d2eba27b9bfc77ef6b40e5721713846815418b#commitcomment-34519739 + +In add_crypttab_devices() split_keyspec is called on the keyfile argument, +which may be NULL. + +(cherry picked from commit fef716b28be6e866b8afe995805d5ebe2af6bbfa) + +Related: #1763155 +--- + src/cryptsetup/cryptsetup-generator.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index 7b234e37be..a09983b576 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -44,10 +44,14 @@ static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_key + _cleanup_free_ char *keyfile = NULL, *keydev = NULL; + const char *c; + +- assert(keyspec); + assert(ret_keyfile); + assert(ret_keydev); + ++ if (!keyspec) { ++ *ret_keyfile = *ret_keydev = NULL; ++ return 0; ++ } ++ + c = strrchr(keyspec, ':'); + if (c) { + keyfile = strndup(keyspec, c-keyspec); diff --git a/SOURCES/0301-cryptsetup-generator-guess-whether-the-keyfile-argum.patch b/SOURCES/0301-cryptsetup-generator-guess-whether-the-keyfile-argum.patch new file mode 100644 index 0000000..0f394ac --- /dev/null +++ b/SOURCES/0301-cryptsetup-generator-guess-whether-the-keyfile-argum.patch @@ -0,0 +1,100 @@ +From 9040e15cd3cba546b47aeae0ea133afa1a6ad292 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 13 Nov 2019 10:32:30 +0100 +Subject: [PATCH] cryptsetup-generator: guess whether the keyfile argument is + two items or one + +Fixes #13615. + +See the inline comment for documentation. + +(cherry picked from commit 32c6237a7c2e697d2fc4f3403319db16858fb8e3) + +Related: #1763155 +--- + src/cryptsetup/cryptsetup-generator.c | 45 ++++++++++++++++++--------- + 1 file changed, 30 insertions(+), 15 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index a09983b576..4117930925 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -54,17 +54,36 @@ static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_key + + c = strrchr(keyspec, ':'); + if (c) { +- keyfile = strndup(keyspec, c-keyspec); +- keydev = strdup(c + 1); +- if (!keyfile || !keydev) ++ /* The keydev part has to be either an absolute path to device node (/dev/something, ++ * /dev/foo/something, or even possibly /dev/foo/something:part), or a fstab device ++ * specification starting with LABEL= or similar. The keyfile part has the same syntax. ++ * ++ * Let's try to guess if the second part looks like a keydev specification, or just part of a ++ * filename with a colon. fstab_node_to_udev_node() will convert the fstab device syntax to ++ * an absolute path. If we didn't get an absolute path, assume that it is just part of the ++ * first keyfile argument. */ ++ ++ keydev = fstab_node_to_udev_node(c + 1); ++ if (!keydev) + return log_oom(); +- } else { ++ ++ if (path_is_absolute(keydev)) ++ keyfile = strndup(keyspec, c-keyspec); ++ else { ++ log_debug("Keyspec argument contains a colon, but \"%s\" doesn't look like a device specification.\n" ++ "Assuming that \"%s\" is a single device specification.", ++ c + 1, keyspec); ++ keydev = mfree(keydev); ++ c = NULL; ++ } ++ } ++ ++ if (!c) + /* No keydev specified */ + keyfile = strdup(keyspec); +- keydev = NULL; +- if (!keyfile) +- return log_oom(); +- } ++ ++ if (!keyfile) ++ return log_oom(); + + *ret_keyfile = TAKE_PTR(keyfile); + *ret_keydev = TAKE_PTR(keydev); +@@ -73,7 +92,7 @@ static int split_keyspec(const char *keyspec, char **ret_keyfile, char **ret_key + } + + static int generate_keydev_mount(const char *name, const char *keydev, const char *keydev_timeout, bool canfail, char **unit, char **mount) { +- _cleanup_free_ char *u = NULL, *what = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL; ++ _cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + usec_t timeout_us; +@@ -111,22 +130,18 @@ static int generate_keydev_mount(const char *name, const char *keydev, const cha + if (r < 0) + return r; + +- what = fstab_node_to_udev_node(keydev); +- if (!what) +- return -ENOMEM; +- + fprintf(f, + "[Unit]\n" + "DefaultDependencies=no\n\n" + "[Mount]\n" + "What=%s\n" + "Where=%s\n" +- "Options=ro%s\n", what, where, canfail ? ",nofail" : ""); ++ "Options=ro%s\n", keydev, where, canfail ? ",nofail" : ""); + + if (keydev_timeout) { + r = parse_sec_fix_0(keydev_timeout, &timeout_us); + if (r >= 0) { +- r = unit_name_from_path(what, ".device", &device_unit); ++ r = unit_name_from_path(keydev, ".device", &device_unit); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + diff --git a/SOURCES/0302-crypt-util-Translate-libcryptsetup-log-level-instead.patch b/SOURCES/0302-crypt-util-Translate-libcryptsetup-log-level-instead.patch new file mode 100644 index 0000000..f7c2454 --- /dev/null +++ b/SOURCES/0302-crypt-util-Translate-libcryptsetup-log-level-instead.patch @@ -0,0 +1,46 @@ +From 05e184dea3f0182e5787812adfd52b68cff9418d Mon Sep 17 00:00:00 2001 +From: Jan Janssen +Date: Mon, 25 Jun 2018 20:33:31 +0200 +Subject: [PATCH] crypt-util: Translate libcryptsetup log level instead of + using log_debug() + +This makes sure that errors reported by libcryptsetup are shown to the +user instead of getting swallowed up by log_debug(). + +(cherry picked from commit aa2cc005d77890b07e8c579f25e1333ff8ba8dac) + +Resolves: #1776408 +--- + src/basic/crypt-util.c | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/src/basic/crypt-util.c b/src/basic/crypt-util.c +index b181ba3ba0..20bdc5489e 100644 +--- a/src/basic/crypt-util.c ++++ b/src/basic/crypt-util.c +@@ -5,6 +5,24 @@ + #include "log.h" + + void cryptsetup_log_glue(int level, const char *msg, void *usrptr) { +- log_debug("%s", msg); ++ switch (level) { ++ case CRYPT_LOG_NORMAL: ++ level = LOG_NOTICE; ++ break; ++ case CRYPT_LOG_ERROR: ++ level = LOG_ERR; ++ break; ++ case CRYPT_LOG_VERBOSE: ++ level = LOG_INFO; ++ break; ++ case CRYPT_LOG_DEBUG: ++ level = LOG_DEBUG; ++ break; ++ default: ++ log_error("Unknown libcryptsetup log level: %d", level); ++ level = LOG_ERR; ++ } ++ ++ log_full(level, "%s", msg); + } + #endif diff --git a/SOURCES/0303-cryptsetup-add-some-commenting-about-EAGAIN-generati.patch b/SOURCES/0303-cryptsetup-add-some-commenting-about-EAGAIN-generati.patch new file mode 100644 index 0000000..e8f6f83 --- /dev/null +++ b/SOURCES/0303-cryptsetup-add-some-commenting-about-EAGAIN-generati.patch @@ -0,0 +1,25 @@ +From ea0c4c31f6dff7d01e585bd8d5f962b373844544 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 21 Jan 2019 20:13:11 +0100 +Subject: [PATCH] cryptsetup: add some commenting about EAGAIN generation + +(cherry picked from commit b7a0fead10959b03a1fa642a5ae7aca3a6a3dee9) + +Related: #1776408 +--- + src/cryptsetup/cryptsetup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 0881aea915..f2b2557497 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -455,7 +455,7 @@ static int attach_tcrypt( + r = read_one_line_file(key_file, &passphrase); + if (r < 0) { + log_error_errno(r, "Failed to read password file '%s': %m", key_file); +- return -EAGAIN; ++ return -EAGAIN; /* log with the actual error, but return EAGAIN */ + } + + params.passphrase = passphrase; diff --git a/SOURCES/0304-cryptsetup-downgrade-a-log-message-we-ignore.patch b/SOURCES/0304-cryptsetup-downgrade-a-log-message-we-ignore.patch new file mode 100644 index 0000000..cd853ee --- /dev/null +++ b/SOURCES/0304-cryptsetup-downgrade-a-log-message-we-ignore.patch @@ -0,0 +1,25 @@ +From 3bbacfb22a9266769a41dee6f8f594fbeb6287fc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 21 Jan 2019 20:19:57 +0100 +Subject: [PATCH] cryptsetup: downgrade a log message we ignore + +(cherry picked from commit 44ce4255147ab308c1f13580147c693204c322e8) + +Related: #1776408 +--- + src/cryptsetup/cryptsetup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index f2b2557497..53fe04a73f 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -621,7 +621,7 @@ int main(int argc, char *argv[]) { + !streq(argv[4], "none")) { + + if (!path_is_absolute(argv[4])) +- log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]); ++ log_warning("Password file path '%s' is not absolute. Ignoring.", argv[4]); + else + key_file = argv[4]; + } diff --git a/SOURCES/0305-cryptsetup-rework-how-we-log-about-activation-failur.patch b/SOURCES/0305-cryptsetup-rework-how-we-log-about-activation-failur.patch new file mode 100644 index 0000000..5b357c3 --- /dev/null +++ b/SOURCES/0305-cryptsetup-rework-how-we-log-about-activation-failur.patch @@ -0,0 +1,102 @@ +From 966ecf0011a02c7823083a7868b8589fdf850be8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 21 Jan 2019 20:20:35 +0100 +Subject: [PATCH] cryptsetup: rework how we log about activation failures + +First of all let's always log where the errors happen, and not in an +upper stackframe, in all cases. Previously we'd do this somethis one way +and sometimes another, which resulted in sometimes duplicate logging and +sometimes none. + +When we cannot activate something due to bad password the kernel gives +us EPERM. Let's uniformly return this EAGAIN, so tha the next password +is tried. (previously this was done in most cases but not in all) + +When we get EPERM let's also explicitly indicate that this probably +means the password is simply wrong. + +Fixes: #11498 +(cherry picked from commit 6f177c7dc092eb68762b4533d41b14244adb2a73) + +Related: #1776408 +--- + src/cryptsetup/cryptsetup.c | 36 ++++++++++++++++++++++-------------- + 1 file changed, 22 insertions(+), 14 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 53fe04a73f..33c215eaa1 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -469,10 +469,15 @@ static int attach_tcrypt( + log_error("Failed to activate using password file '%s'.", key_file); + return -EAGAIN; + } +- return r; ++ ++ return log_error_errno(r, "Failed to load tcrypt superblock on device %s: %m", crypt_get_device_name(cd)); + } + +- return crypt_activate_by_volume_key(cd, name, NULL, 0, flags); ++ r = crypt_activate_by_volume_key(cd, name, NULL, 0, flags); ++ if (r < 0) ++ return log_error_errno(r, "Failed to activate tcrypt device %s: %m", crypt_get_device_name(cd)); ++ ++ return 0; + } + + static int attach_luks_or_plain(struct crypt_device *cd, +@@ -549,22 +554,30 @@ static int attach_luks_or_plain(struct crypt_device *cd, + + if (key_file) { + r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); +- if (r < 0) { +- log_error_errno(r, "Failed to activate with key file '%s': %m", key_file); +- return -EAGAIN; ++ if (r == -EPERM) { ++ 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 < 0) ++ return log_error_errno(r, "Failed to activate with key file '%s': %m", key_file); + } else { + char **p; + ++ r = -EINVAL; + STRV_FOREACH(p, passwords) { + if (pass_volume_key) + r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags); + else + r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags); +- + if (r >= 0) + break; + } ++ if (r == -EPERM) { ++ log_error_errno(r, "Failed to activate with specified passphrase. (Passphrase incorrect?)"); ++ return -EAGAIN; /* log actual error, but return EAGAIN */ ++ } ++ if (r < 0) ++ return log_error_errno(r, "Failed to activate with specified passphrase: %m"); + } + + return r; +@@ -726,16 +739,11 @@ int main(int argc, char *argv[]) { + flags); + if (r >= 0) + break; +- if (r == -EAGAIN) { +- key_file = NULL; +- continue; +- } +- if (r != -EPERM) { +- log_error_errno(r, "Failed to activate: %m"); ++ if (r != -EAGAIN) + goto finish; +- } + +- log_warning("Invalid passphrase."); ++ /* Passphrase not correct? Let's try again! */ ++ key_file = NULL; + } + + if (arg_tries != 0 && tries >= arg_tries) { diff --git a/SOURCES/0306-rules-reintroduce-60-alias-kmsg.rules.patch b/SOURCES/0306-rules-reintroduce-60-alias-kmsg.rules.patch new file mode 100644 index 0000000..2451c00 --- /dev/null +++ b/SOURCES/0306-rules-reintroduce-60-alias-kmsg.rules.patch @@ -0,0 +1,41 @@ +From b7f9d757dd6f276203b8b04f0c0ba1c61bcf8937 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 18 Dec 2019 09:41:29 +0100 +Subject: [PATCH] rules: reintroduce 60-alias-kmsg.rules + +Resolves:#1739353 +rhel-only +--- + rules/60-alias-kmsg.rules | 10 ++++++++++ + rules/meson.build | 1 + + 2 files changed, 11 insertions(+) + create mode 100644 rules/60-alias-kmsg.rules + +diff --git a/rules/60-alias-kmsg.rules b/rules/60-alias-kmsg.rules +new file mode 100644 +index 0000000000..9c7236a730 +--- /dev/null ++++ b/rules/60-alias-kmsg.rules +@@ -0,0 +1,10 @@ ++SUBSYSTEM!="block", GOTO="log_end" ++KERNEL=="loop*|ram*", GOTO="log_end" ++ACTION=="remove", GOTO="log_end" ++ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="log_end" ++ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}=="1", GOTO="log_end" ++ ++IMPORT{cmdline}="udev.alias" ++ENV{udev.alias}=="1", RUN+="/bin/sh -c 'echo udev-alias: $name \($links\) > /dev/kmsg'" ++ ++LABEL="log_end" +diff --git a/rules/meson.build b/rules/meson.build +index 6363f8bf2e..7b5b2472de 100644 +--- a/rules/meson.build ++++ b/rules/meson.build +@@ -3,6 +3,7 @@ + rules = files(''' + 40-redhat.rules + 40-elevator.rules ++ 60-alias-kmsg.rules + 60-block.rules + 60-cdrom_id.rules + 60-drm.rules diff --git a/SOURCES/0307-sd-bus-make-rqueue-wqueue-sizes-of-type-size_t.patch b/SOURCES/0307-sd-bus-make-rqueue-wqueue-sizes-of-type-size_t.patch new file mode 100644 index 0000000..65374a7 --- /dev/null +++ b/SOURCES/0307-sd-bus-make-rqueue-wqueue-sizes-of-type-size_t.patch @@ -0,0 +1,49 @@ +From 1d8e642b0b67f07b0bf469c25126b878380bae6a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 18:13:03 +0100 +Subject: [PATCH] sd-bus: make rqueue/wqueue sizes of type size_t + +Let's do this like we usually do and size arrays with size_t. + +We already do this for the "allocated" counter correctly, and externally +we expose the queue sizes as uint64_t anyway, hence there's really no +point in usigned "unsigned" internally. + +(cherry picked from commit 143d4e045a798ccc87889b2a8a60d7fbe44be441) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/bus-internal.h | 4 ++-- + src/libsystemd/sd-bus/sd-bus.c | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h +index 5d773b14c4..06bd7862cb 100644 +--- a/src/libsystemd/sd-bus/bus-internal.h ++++ b/src/libsystemd/sd-bus/bus-internal.h +@@ -221,11 +221,11 @@ struct sd_bus { + 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; + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 1c9e967ae0..64026f7ee1 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -2080,7 +2080,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); diff --git a/SOURCES/0308-sd-bus-reorder-bus-ref-and-bus-message-ref-handling.patch b/SOURCES/0308-sd-bus-reorder-bus-ref-and-bus-message-ref-handling.patch new file mode 100644 index 0000000..89004d8 --- /dev/null +++ b/SOURCES/0308-sd-bus-reorder-bus-ref-and-bus-message-ref-handling.patch @@ -0,0 +1,51 @@ +From 9c23ceef0a08ffdf4aed7a96ec440e1b110568ac Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 18:14:17 +0100 +Subject: [PATCH] sd-bus: reorder bus ref and bus message ref handling + +Let's always place handling of these references together, so that all +reference counting during allocation is at a single place. + +(cherry picked from commit e593b6a87a335267e5f7238b14683b7f840a01a3) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/bus-message.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 19cb2b9a97..e9cdf46c91 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -461,7 +461,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; +@@ -515,7 +514,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; +@@ -588,13 +589,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; diff --git a/SOURCES/0309-sd-bus-make-sure-dispatch_rqueue-initializes-return-.patch b/SOURCES/0309-sd-bus-make-sure-dispatch_rqueue-initializes-return-.patch new file mode 100644 index 0000000..9c7b804 --- /dev/null +++ b/SOURCES/0309-sd-bus-make-sure-dispatch_rqueue-initializes-return-.patch @@ -0,0 +1,32 @@ +From 19a9c67b79ebb9a65bc2aec8d8f2799262ef0cb2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 18:15:37 +0100 +Subject: [PATCH] sd-bus: make sure dispatch_rqueue() initializes return + parameter on all types of success + +Let's make sure our own code follows coding style and initializes all +return values on all types of success (and leaves it uninitialized in +all types of failure). + +(cherry picked from commit c0bc4ec5cc17ac61773d1e9362b0ffa8382c1ff1) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/sd-bus.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 64026f7ee1..55b008cc9f 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -1814,8 +1814,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; + } diff --git a/SOURCES/0310-sd-bus-drop-two-inappropriate-empty-lines.patch b/SOURCES/0310-sd-bus-drop-two-inappropriate-empty-lines.patch new file mode 100644 index 0000000..da2330b --- /dev/null +++ b/SOURCES/0310-sd-bus-drop-two-inappropriate-empty-lines.patch @@ -0,0 +1,31 @@ +From 7e9944795e3f0046857379a5f878b365597ed373 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 18:18:18 +0100 +Subject: [PATCH] sd-bus: drop two inappropriate empty lines + +(cherry picked from commit 39feb2ce417e54cf9746e64b5dfd610cef6ac440) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/sd-bus.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 55b008cc9f..01060d105c 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -2634,7 +2634,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; + +@@ -2758,7 +2757,6 @@ static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd + return r; + + *ret = TAKE_PTR(m); +- + return 1; + } + diff --git a/SOURCES/0311-sd-bus-initialize-mutex-after-we-allocated-the-wqueu.patch b/SOURCES/0311-sd-bus-initialize-mutex-after-we-allocated-the-wqueu.patch new file mode 100644 index 0000000..d91eb20 --- /dev/null +++ b/SOURCES/0311-sd-bus-initialize-mutex-after-we-allocated-the-wqueu.patch @@ -0,0 +1,33 @@ +From 247d4f826ab189c4dfc4706aaa94782342655218 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 21:06:30 +0100 +Subject: [PATCH] sd-bus: initialize mutex after we allocated the wqueue + +That way the mutex doesn't have to be destroyed when we exit early due +to OOM. + +(cherry picked from commit 2fe9a10d7695c4c3a748969a0d1662c624e50e5e) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/sd-bus.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index 01060d105c..e49d58137d 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -248,12 +248,12 @@ _public_ int sd_bus_new(sd_bus **ret) { + b->original_pid = getpid_cached(); + b->n_groups = (size_t) -1; + +- 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; + } diff --git a/SOURCES/0312-sd-bus-always-go-through-sd_bus_unref-to-free-messag.patch b/SOURCES/0312-sd-bus-always-go-through-sd_bus_unref-to-free-messag.patch new file mode 100644 index 0000000..4e2883a --- /dev/null +++ b/SOURCES/0312-sd-bus-always-go-through-sd_bus_unref-to-free-messag.patch @@ -0,0 +1,74 @@ +From 6180d5ee908c9c742f816c6922c229aefd533117 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 21:07:42 +0100 +Subject: [PATCH] sd-bus: always go through sd_bus_unref() to free messages + +Don't try to be smart, don't bypass the ref counting logic if there's no +real reason to. + +This matters if we want to tweak the ref counting logic later. + +(cherry picked from commit b41812d1e308de03c879cfca490105216d528c4b) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/bus-message.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index e9cdf46c91..306b6d6816 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -138,8 +138,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; +@@ -531,7 +529,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; + +@@ -651,7 +649,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); +@@ -696,7 +694,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; + +@@ -747,7 +745,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); +@@ -850,7 +848,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); diff --git a/SOURCES/0313-bus-message-introduce-two-kinds-of-references-to-bus.patch b/SOURCES/0313-bus-message-introduce-two-kinds-of-references-to-bus.patch new file mode 100644 index 0000000..7eb554d --- /dev/null +++ b/SOURCES/0313-bus-message-introduce-two-kinds-of-references-to-bus.patch @@ -0,0 +1,182 @@ +From bc2d7df4fc21e9e54413169d5aad21616314d65e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 18:18:54 +0100 +Subject: [PATCH] bus-message: introduce two kinds of references to bus + messages + +Before this commit bus messages had a single reference count: when it +reached zero the message would be freed. This simple approach meant a +cyclic dependency was typically seen: a message that was enqueued in a +bus connection object would reference the bus connection object but also +itself be referenced by the bus connection object. So far out strategy +to avoid cases like this was: make sure to process the bus connection +regularly so that messages don#t stay queued, and at exit flush/close +the connection so that the message queued would be emptied, and thus the +cyclic dependencies resolved. Im many cases this isn't done properly +however. + +With this change, let's address the issue more systematically: let's +break the reference cycle. Specifically, there are now two types of +references to a bus message: + +1. A regular one, which keeps both the message and the bus object it is + associated with pinned. + +2. A "queue" reference, which is weaker: it pins the message, but not + the bus object it is associated with. + +The idea is then that regular user handling uses regular references, but +when a message is enqueued on its connection, then this takes a "queue" +reference instead. This then means that a queued message doesn't imply +the connection itself remains pinned, only regular references to the +connection or a message associated with it do. Thus, if we end up in the +situation where a user allocates a bus and a message and enqueues the +latter in the former and drops all refs to both, then this will detect +this case and free both. + +Note that this scheme isn't perfect, it only covers references between +messages and the busses they are associated with. If OTOH a bus message +is enqueued on a different bus than it is associated with cyclic deps +cannot be recognized with this simple algorithm, and thus if you enqueue +a message associated with a bus A on a bus B, and another message +associated with bus B on a bus A, a cyclic ref will be in effect and not +be discovered. However, given that this is an exotic case (though one +that happens, consider systemd-bus-stdio-bridge), it should be OK not to +cover with this, and people have to explicit flush all queues on exit in +that case. + +Note that this commit only establishes the separate reference counters +per message. A follow-up commit will start making use of this from the +bus connection object. + +(cherry picked from commit 1b3f9dd759ca0ea215e7b89f8ce66d1b724497b9) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/bus-message.c | 60 ++++++++++++++++++++++++++--- + src/libsystemd/sd-bus/bus-message.h | 14 ++++++- + 2 files changed, 68 insertions(+), 6 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c +index 306b6d6816..7fe8929f82 100644 +--- a/src/libsystemd/sd-bus/bus-message.c ++++ b/src/libsystemd/sd-bus/bus-message.c +@@ -120,7 +120,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); +@@ -893,27 +894,76 @@ int bus_message_new_synthetic_error( + } + + _public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { +- + if (!m) + return NULL; + +- assert(m->n_ref > 0); ++ /* 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) ++ 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); + } + +diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h +index 97f6060e30..ded88005e2 100644 +--- a/src/libsystemd/sd-bus/bus-message.h ++++ b/src/libsystemd/sd-bus/bus-message.h +@@ -51,7 +51,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; + +@@ -216,3 +225,6 @@ int bus_message_append_sender(sd_bus_message *m, const char *sender); + + void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m); + void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m); ++ ++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/SOURCES/0314-sd-bus-introduce-API-for-re-enqueuing-incoming-messa.patch b/SOURCES/0314-sd-bus-introduce-API-for-re-enqueuing-incoming-messa.patch new file mode 100644 index 0000000..fdafc3d --- /dev/null +++ b/SOURCES/0314-sd-bus-introduce-API-for-re-enqueuing-incoming-messa.patch @@ -0,0 +1,74 @@ +From 8efdae75ddf035c8c04983820f8d8cf767cc17b1 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Fri, 31 Jan 2020 11:34:45 +0100 +Subject: [PATCH] sd-bus: introduce API for re-enqueuing incoming messages + +When authorizing via PolicyKit we want to process incoming method calls +twice: once to process and figure out that we need PK authentication, +and a second time after we aquired PK authentication to actually execute +the operation. With this new call sd_bus_enqueue_for_read() we have a +way to put an incoming message back into the read queue for this +purpose. + +This might have other uses too, for example debugging. +Related: CVE-2020-1712 +--- + src/libsystemd/libsystemd.sym | 1 + + src/libsystemd/sd-bus/sd-bus.c | 24 ++++++++++++++++++++++++ + src/systemd/sd-bus.h | 1 + + 3 files changed, 26 insertions(+) + +diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym +index 1eec17db50..e9972593a6 100644 +--- a/src/libsystemd/libsystemd.sym ++++ b/src/libsystemd/libsystemd.sym +@@ -569,4 +569,5 @@ global: + sd_event_source_get_inotify_mask; + sd_event_source_set_destroy_callback; + sd_event_source_get_destroy_callback; ++ sd_bus_enqueue_for_read; + } LIBSYSTEMD_238; +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index e49d58137d..68ad6cbe89 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -4120,3 +4120,27 @@ _public_ int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret) { + *ret = bus->wqueue_size; + return 0; + } ++ ++_public_ int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m) { ++ int r; ++ ++ assert_return(bus, -EINVAL); ++ assert_return(bus = bus_resolve(bus), -ENOPKG); ++ assert_return(m, -EINVAL); ++ assert_return(m->sealed, -EINVAL); ++ assert_return(!bus_pid_changed(bus), -ECHILD); ++ ++ if (!BUS_IS_OPEN(bus->state)) ++ return -ENOTCONN; ++ ++ /* Re-enqeue a message for reading. This is primarily useful for PolicyKit-style authentication, ++ * where we want accept a message, then determine we need to interactively authenticate the user, and ++ * when we have that process the message again. */ ++ ++ r = bus_rqueue_make_room(bus); ++ if (r < 0) ++ return r; ++ ++ bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(m, bus); ++ return 0; ++} +diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h +index 54c4b1ca83..9ba757b13d 100644 +--- a/src/systemd/sd-bus.h ++++ b/src/systemd/sd-bus.h +@@ -193,6 +193,7 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **r); + int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r); + int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); + int sd_bus_flush(sd_bus *bus); ++int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m); + + sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus); + sd_bus_message* sd_bus_get_current_message(sd_bus *bus); diff --git a/SOURCES/0315-sd-event-add-sd_event_source_disable_unref-helper.patch b/SOURCES/0315-sd-event-add-sd_event_source_disable_unref-helper.patch new file mode 100644 index 0000000..1748190 --- /dev/null +++ b/SOURCES/0315-sd-event-add-sd_event_source_disable_unref-helper.patch @@ -0,0 +1,130 @@ +From 73b87f8c73af714a32e7b56b217cd4e4f46a5ea7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 8 May 2019 14:39:57 +0200 +Subject: [PATCH] sd-event: add sd_event_source_disable_unref() helper + +(cherry picked from commit afd15bbb4b6414b9356799c63029e36642dae8e4) +Related: CVE-2020-1712 +--- + man/rules/meson.build | 4 +++- + man/sd_event_source_unref.xml | 30 +++++++++++++++++++----------- + src/libsystemd/libsystemd.sym | 1 + + src/libsystemd/sd-event/sd-event.c | 6 ++++++ + src/systemd/sd-event.h | 1 + + 5 files changed, 30 insertions(+), 12 deletions(-) + +diff --git a/man/rules/meson.build b/man/rules/meson.build +index 989d11c9b9..7ae94ea265 100644 +--- a/man/rules/meson.build ++++ b/man/rules/meson.build +@@ -340,7 +340,9 @@ manpages = [ + ['sd_event_source_set_userdata', '3', ['sd_event_source_get_userdata'], ''], + ['sd_event_source_unref', + '3', +- ['sd_event_source_ref', 'sd_event_source_unrefp'], ++ ['sd_event_source_disable_unref', ++ 'sd_event_source_ref', ++ 'sd_event_source_unrefp'], + ''], + ['sd_event_wait', + '3', +diff --git a/man/sd_event_source_unref.xml b/man/sd_event_source_unref.xml +index d1b83c57aa..af8fed33f2 100644 +--- a/man/sd_event_source_unref.xml ++++ b/man/sd_event_source_unref.xml +@@ -22,6 +22,7 @@ + sd_event_source_unref + sd_event_source_unrefp + sd_event_source_ref ++ sd_event_source_disable_unref + + Increase or decrease event source reference counters + +@@ -45,6 +46,10 @@ + sd_event_source *source + + ++ ++ sd_event_source* sd_event_source_disable_unref ++ sd_event_source *source ++ + + + +@@ -80,23 +85,26 @@ + the passed event source object is + NULL. + +- Note that event source objects stay alive and may be +- dispatched as long as they have a reference counter greater than +- zero. In order to drop a reference of an event source and make +- sure the associated event source handler function is not called +- anymore it is recommended to combine a call of ++ Note that event source objects stay alive and may be dispatched as long as they have a reference ++ counter greater than zero. In order to drop a reference of an event source and make sure the associated ++ event source handler function is not called anymore it is recommended to combine a call of + sd_event_source_unref() with a prior call to +- sd_event_source_set_enabled() with +- SD_EVENT_OFF. ++ sd_event_source_set_enabled() with SD_EVENT_OFF or call ++ sd_event_source_disable_unref(), see below. ++ ++ sd_event_source_disable_unref() combines a call to ++ sd_event_source_set_enabled() with SD_EVENT_OFF with ++ sd_event_source_unref(). This ensures that the source is disabled before the local ++ reference to it is lost. The source parameter is allowed to be ++ NULL. + + + + Return Value + +- sd_event_source_unref() always returns +- NULL. +- sd_event_source_ref() always returns the +- event source object passed in. ++ sd_event_source_unref() and ++ sd_event_source_disable_unref() always return NULL. ++ sd_event_source_ref() always returns the event source object passed in. + + + +diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym +index e9972593a6..778e88a16c 100644 +--- a/src/libsystemd/libsystemd.sym ++++ b/src/libsystemd/libsystemd.sym +@@ -570,4 +570,5 @@ global: + sd_event_source_set_destroy_callback; + sd_event_source_get_destroy_callback; + sd_bus_enqueue_for_read; ++ sd_event_source_disable_unref; + } LIBSYSTEMD_238; +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index d53b9a7026..0d3bf5cbb6 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -580,6 +580,12 @@ _public_ sd_event* sd_event_unref(sd_event *e) { + return NULL; + } + ++_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) { ++ if (s) ++ (void) sd_event_source_set_enabled(s, SD_EVENT_OFF); ++ return sd_event_source_unref(s); ++} ++ + static bool event_pid_changed(sd_event *e) { + assert(e); + +diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h +index 7fcae4ac49..9876be01c6 100644 +--- a/src/systemd/sd-event.h ++++ b/src/systemd/sd-event.h +@@ -113,6 +113,7 @@ int sd_event_get_iteration(sd_event *e, uint64_t *ret); + + sd_event_source* sd_event_source_ref(sd_event_source *s); + sd_event_source* sd_event_source_unref(sd_event_source *s); ++sd_event_source* sd_event_source_disable_unref(sd_event_source *s); + + sd_event *sd_event_source_get_event(sd_event_source *s); + void* sd_event_source_get_userdata(sd_event_source *s); diff --git a/SOURCES/0316-polkit-when-authorizing-via-PK-let-s-re-resolve-call.patch b/SOURCES/0316-polkit-when-authorizing-via-PK-let-s-re-resolve-call.patch new file mode 100644 index 0000000..da3d850 --- /dev/null +++ b/SOURCES/0316-polkit-when-authorizing-via-PK-let-s-re-resolve-call.patch @@ -0,0 +1,156 @@ +From 2ec3c78b1d1ba907cd888aac3cdc3a86c03cda90 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Fri, 31 Jan 2020 15:17:25 +0100 +Subject: [PATCH] polkit: when authorizing via PK let's re-resolve + callback/userdata instead of caching it + +Previously, when doing an async PK query we'd store the original +callback/userdata pair and call it again after the PK request is +complete. This is problematic, since PK queries might be slow and in the +meantime the userdata might be released and re-acquired. Let's avoid +this by always traversing through the message handlers so that we always +re-resolve the callback and userdata pair and thus can be sure it's +up-to-date and properly valid. + +Resolves: CVE-2020-1712 +--- + src/shared/bus-util.c | 74 +++++++++++++++++++++++++++++-------------- + 1 file changed, 50 insertions(+), 24 deletions(-) + +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index 2d908eb45c..5ed68429be 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -319,10 +319,10 @@ int bus_test_polkit( + + typedef struct AsyncPolkitQuery { + sd_bus_message *request, *reply; +- sd_bus_message_handler_t callback; +- void *userdata; + sd_bus_slot *slot; ++ + Hashmap *registry; ++ sd_event_source *defer_event_source; + } AsyncPolkitQuery; + + static void async_polkit_query_free(AsyncPolkitQuery *q) { +@@ -338,9 +338,22 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) { + sd_bus_message_unref(q->request); + sd_bus_message_unref(q->reply); + ++ sd_event_source_disable_unref(q->defer_event_source); + free(q); + } + ++static int async_polkit_defer(sd_event_source *s, void *userdata) { ++ AsyncPolkitQuery *q = userdata; ++ ++ assert(s); ++ ++ /* This is called as idle event source after we processed the async polkit reply, hopefully after the ++ * method call we re-enqueued has been properly processed. */ ++ ++ async_polkit_query_free(q); ++ return 0; ++} ++ + static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; + AsyncPolkitQuery *q = userdata; +@@ -349,19 +362,45 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e + assert(reply); + assert(q); + ++ assert(q->slot); + q->slot = sd_bus_slot_unref(q->slot); ++ ++ assert(!q->reply); + q->reply = sd_bus_message_ref(reply); + ++ /* Now, let's dispatch the original message a second time be re-enqueing. This will then traverse the ++ * whole message processing again, and thus re-validating and re-retrieving the "userdata" field ++ * again. ++ * ++ * We install an idle event loop event to clean-up the PolicyKit request data when we are idle again, ++ * i.e. after the second time the message is processed is complete. */ ++ ++ assert(!q->defer_event_source); ++ r = sd_event_add_defer(sd_bus_get_event(sd_bus_message_get_bus(reply)), &q->defer_event_source, async_polkit_defer, q); ++ if (r < 0) ++ goto fail; ++ ++ r = sd_event_source_set_priority(q->defer_event_source, SD_EVENT_PRIORITY_IDLE); ++ if (r < 0) ++ goto fail; ++ ++ r = sd_event_source_set_enabled(q->defer_event_source, SD_EVENT_ONESHOT); ++ if (r < 0) ++ goto fail; ++ + r = sd_bus_message_rewind(q->request, true); +- if (r < 0) { +- r = sd_bus_reply_method_errno(q->request, r, NULL); +- goto finish; +- } ++ if (r < 0) ++ goto fail; + +- r = q->callback(q->request, q->userdata, &error_buffer); +- r = bus_maybe_reply_error(q->request, r, &error_buffer); ++ r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request); ++ if (r < 0) ++ goto fail; + +-finish: ++ return 1; ++ ++fail: ++ log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m"); ++ (void) sd_bus_reply_method_errno(q->request, r, NULL); + async_polkit_query_free(q); + + return r; +@@ -382,11 +421,9 @@ int bus_verify_polkit_async( + #if ENABLE_POLKIT + _cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL; + AsyncPolkitQuery *q; +- const char *sender, **k, **v; +- sd_bus_message_handler_t callback; +- void *userdata; + int c; + #endif ++ const char *sender, **k, **v; + int r; + + assert(call); +@@ -444,20 +481,11 @@ int bus_verify_polkit_async( + else if (r > 0) + return 1; + +-#if ENABLE_POLKIT +- if (sd_bus_get_current_message(call->bus) != call) +- return -EINVAL; +- +- callback = sd_bus_get_current_handler(call->bus); +- if (!callback) +- return -EINVAL; +- +- userdata = sd_bus_get_current_userdata(call->bus); +- + sender = sd_bus_message_get_sender(call); + if (!sender) + return -EBADMSG; + ++#if ENABLE_POLKIT + c = sd_bus_message_get_allow_interactive_authorization(call); + if (c < 0) + return c; +@@ -509,8 +537,6 @@ int bus_verify_polkit_async( + return -ENOMEM; + + q->request = sd_bus_message_ref(call); +- q->callback = callback; +- q->userdata = userdata; + + r = hashmap_put(*registry, call, q); + if (r < 0) { diff --git a/SOURCES/0317-sysctl-let-s-by-default-increase-the-numeric-PID-ran.patch b/SOURCES/0317-sysctl-let-s-by-default-increase-the-numeric-PID-ran.patch new file mode 100644 index 0000000..d7523ce --- /dev/null +++ b/SOURCES/0317-sysctl-let-s-by-default-increase-the-numeric-PID-ran.patch @@ -0,0 +1,70 @@ +From b9be2c6b48227642ba85c5a741f121cc99655904 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 6 Jan 2020 12:30:58 +0100 +Subject: [PATCH] sysctl: let's by default increase the numeric PID range from + 2^16 to 2^22 + +This should PID collisions a tiny bit less likely, and thus improve +security and robustness. + +2^22 isn't particularly a lot either, but it's the current kernel +limitation. + +Bumping this limit was suggested by Linus himself: + +https://lwn.net/ml/linux-kernel/CAHk-=wiZ40LVjnXSi9iHLE_-ZBsWFGCgdmNiYZUXn1-V5YBg2g@mail.gmail.com/ + +Let's experiment with this in systemd upstream first. Downstreams and +users can after all still comment this easily. + +Besides compat concern the most often heard issue with such high PIDs is +usability, since they are potentially hard to type. I am not entirely sure though +whether 4194304 (as largest new PID) is that much worse to type or to +copy than 65563. + +This should also simplify management of per system tasks limits as by +this move the sysctl /proc/sys/kernel/threads-max becomes the primary +knob to control how many processes to have in parallel. + +Resolves: #1744214 +--- + sysctl.d/50-pid-max.conf | 17 +++++++++++++++++ + sysctl.d/meson.build | 1 + + 2 files changed, 18 insertions(+) + create mode 100644 sysctl.d/50-pid-max.conf + +diff --git a/sysctl.d/50-pid-max.conf b/sysctl.d/50-pid-max.conf +new file mode 100644 +index 0000000000..3a8393d185 +--- /dev/null ++++ b/sysctl.d/50-pid-max.conf +@@ -0,0 +1,17 @@ ++# 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. ++ ++# See sysctl.d(5) and core(5) for documentation. ++ ++# To override settings in this file, create a local file in /etc ++# (e.g. /etc/sysctl.d/90-override.conf), and put any assignments ++# there. ++ ++# Bump the numeric PID range to its maximum of 2^22 (from the in-kernel default ++# of 2^16), to make PID collisions less likely. ++kernel.pid_max = 4194304 ++ +diff --git a/sysctl.d/meson.build b/sysctl.d/meson.build +index 64f6ce942e..a95957ad7d 100644 +--- a/sysctl.d/meson.build ++++ b/sysctl.d/meson.build +@@ -2,6 +2,7 @@ + + install_data( + '50-default.conf', ++ '50-pid-max.conf', + install_dir : sysctldir) + + in_files = [] diff --git a/SOURCES/0318-journal-do-not-trigger-assertion-when-journal_file_c.patch b/SOURCES/0318-journal-do-not-trigger-assertion-when-journal_file_c.patch new file mode 100644 index 0000000..9ebc71d --- /dev/null +++ b/SOURCES/0318-journal-do-not-trigger-assertion-when-journal_file_c.patch @@ -0,0 +1,49 @@ +From dc4c3a5aa35a5e88adcf210471d9460262c8c0d9 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 28 May 2019 12:40:17 +0900 +Subject: [PATCH] journal: do not trigger assertion when journal_file_close() + get NULL + +We generally expect destructors to not complain if a NULL argument is passed. + +Closes #12400. + +(cherry picked from commit c377a6f3ad3d9bed4ce7e873e8e9ec6b1650c57d) +Resolves: #1788085 +--- + src/journal/journal-file.c | 3 ++- + src/journal/journald-server.c | 7 ++----- + 2 files changed, 4 insertions(+), 6 deletions(-) + +diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c +index efc3ee052b..8249b11b23 100644 +--- a/src/journal/journal-file.c ++++ b/src/journal/journal-file.c +@@ -335,7 +335,8 @@ bool journal_file_is_offlining(JournalFile *f) { + } + + JournalFile* journal_file_close(JournalFile *f) { +- assert(f); ++ if (!f) ++ return NULL; + + #if HAVE_GCRYPT + /* Write the final tag */ +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 6aecb67d6c..6250eab831 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -1906,11 +1906,8 @@ void server_done(Server *s) { + + client_context_flush_all(s); + +- if (s->system_journal) +- (void) journal_file_close(s->system_journal); +- +- if (s->runtime_journal) +- (void) journal_file_close(s->runtime_journal); ++ (void) journal_file_close(s->system_journal); ++ (void) journal_file_close(s->runtime_journal); + + ordered_hashmap_free_with_destructor(s->user_journals, journal_file_close); + diff --git a/SOURCES/0319-journal-use-cleanup-attribute-at-one-more-place.patch b/SOURCES/0319-journal-use-cleanup-attribute-at-one-more-place.patch new file mode 100644 index 0000000..9ca0d60 --- /dev/null +++ b/SOURCES/0319-journal-use-cleanup-attribute-at-one-more-place.patch @@ -0,0 +1,58 @@ +From ceacf935ac9f59bc08b5901f70f227958a2bcf52 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 28 May 2019 18:07:01 +0900 +Subject: [PATCH] journal: use cleanup attribute at one more place + +(cherry picked from commit 627df1dc42b68a74b0882b06366d1185b1a34332) + +Conflicts: + src/journal/journald-server.c + +Related: #1788085 +--- + src/journal/journal-file.h | 1 + + src/journal/journald-server.c | 9 ++++----- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h +index cd8a48a364..6a44fd39d2 100644 +--- a/src/journal/journal-file.h ++++ b/src/journal/journal-file.h +@@ -144,6 +144,7 @@ int journal_file_open( + int journal_file_set_offline(JournalFile *f, bool wait); + bool journal_file_is_offlining(JournalFile *f); + JournalFile* journal_file_close(JournalFile *j); ++DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_close); + + int journal_file_open_reliably( + const char *fname, +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 6250eab831..7632e2d9d0 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -253,8 +253,9 @@ static int open_journal( + bool seal, + JournalMetrics *metrics, + JournalFile **ret) { ++ ++ _cleanup_(journal_file_closep) JournalFile *f = NULL; + int r; +- JournalFile *f; + + assert(s); + assert(fname); +@@ -271,12 +272,10 @@ static int open_journal( + return r; + + r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC); +- if (r < 0) { +- (void) journal_file_close(f); ++ if (r < 0) + return r; +- } + +- *ret = f; ++ *ret = TAKE_PTR(f); + return r; + } + diff --git a/SOURCES/0320-sd-bus-use-queue-message-references-for-managing-r-w.patch b/SOURCES/0320-sd-bus-use-queue-message-references-for-managing-r-w.patch new file mode 100644 index 0000000..f8ff1d5 --- /dev/null +++ b/SOURCES/0320-sd-bus-use-queue-message-references-for-managing-r-w.patch @@ -0,0 +1,183 @@ +From 781a055c17400e953bb7929434fe7a2e6517d5e8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 17 Jan 2019 18:31:59 +0100 +Subject: [PATCH] sd-bus: use "queue" message references for managing r/w + message queues in connection objects + +Let's make use of the new concept the previous commit added. + +See: #4846 +(cherry picked from commit c1757a70eac0382c4837a3833d683919f6a48ed7) +Related: CVE-2020-1712 +--- + src/libsystemd/sd-bus/bus-socket.c | 6 ++- + src/libsystemd/sd-bus/sd-bus.c | 60 ++++++++++++++---------------- + 2 files changed, 32 insertions(+), 34 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c +index 17cfa8e1fd..4a72795d2b 100644 +--- a/src/libsystemd/sd-bus/bus-socket.c ++++ b/src/libsystemd/sd-bus/bus-socket.c +@@ -1116,8 +1116,10 @@ static int bus_socket_make_message(sd_bus *bus, size_t 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 68ad6cbe89..a3509f7e89 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -148,13 +148,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; +@@ -493,7 +493,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; +@@ -1760,7 +1760,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; + +@@ -1789,6 +1789,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; + +@@ -1803,10 +1812,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; + } + +@@ -1884,7 +1891,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; + } +@@ -1898,7 +1905,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: +@@ -2124,37 +2131,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; + } +@@ -2164,15 +2164,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; + } diff --git a/SOURCES/0321-pid1-make-sure-to-restore-correct-default-values-for.patch b/SOURCES/0321-pid1-make-sure-to-restore-correct-default-values-for.patch new file mode 100644 index 0000000..2ca5b14 --- /dev/null +++ b/SOURCES/0321-pid1-make-sure-to-restore-correct-default-values-for.patch @@ -0,0 +1,261 @@ +From 77a273e02c1c811485d13ddca0f844512aed2cff Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 12 Feb 2020 12:58:54 +0100 +Subject: [PATCH] pid1: make sure to restore correct default values for some + rlimits + +Commit fb39af4ce42d7ef9af63009f271f404038703704 forgot to restore the default +rlimit values (RLIMIT_NOFILE and RLIMIT_MEMLOCK) while PID1 is reloading. + +This patch extracts the code in charge of initializing the default values for +those rlimits in order to create dedicated functions, which take care of their +initialization. + +These functions are then called in parse_configuration() so we make sure that +the default values for these rlimits get restored every time PID1 is reloading +its configuration. + +(cherry picked from commit a9fd4cd1206832a61aaf61fff583bb133e6cb965) +Resolves: #1789930 +--- + src/core/main.c | 135 +++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 106 insertions(+), 29 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index c83249a8dc..b8c1e567ad 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -136,7 +136,8 @@ static EmergencyAction arg_cad_burst_action; + static CPUSet arg_cpu_affinity; + static NUMAPolicy arg_numa_policy; + +-static int parse_configuration(void); ++static int parse_configuration(const struct rlimit *saved_rlimit_nofile, ++ const struct rlimit *saved_rlimit_memlock); + + _noreturn_ static void freeze_or_reboot(void) { + +@@ -1149,25 +1150,6 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching + static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { + int r, nr; + +- assert(saved_rlimit); +- +- /* Save the original RLIMIT_NOFILE so that we can reset it +- * later when transitioning from the initrd to the main +- * systemd or suchlike. */ +- if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) +- return log_warning_errno(errno, "Reading RLIMIT_NOFILE failed, ignoring: %m"); +- +- /* Make sure forked processes get the default kernel setting */ +- if (!arg_default_rlimit[RLIMIT_NOFILE]) { +- struct rlimit *rl; +- +- rl = newdup(struct rlimit, saved_rlimit, 1); +- if (!rl) +- return log_oom(); +- +- arg_default_rlimit[RLIMIT_NOFILE] = rl; +- } +- + /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows */ + nr = read_nr_open(); + r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(nr)); +@@ -1180,16 +1162,12 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { + static int bump_rlimit_memlock(struct rlimit *saved_rlimit) { + int r; + +- assert(saved_rlimit); + assert(getuid() == 0); + + /* BPF_MAP_TYPE_LPM_TRIE bpf maps are charged against RLIMIT_MEMLOCK, even though we have CAP_IPC_LOCK which + * should normally disable such checks. We need them to implement IPAccessAllow= and IPAccessDeny=, hence let's + * bump the value high enough for the root user. */ + +- if (getrlimit(RLIMIT_MEMLOCK, saved_rlimit) < 0) +- return log_warning_errno(errno, "Reading RLIMIT_MEMLOCK failed, ignoring: %m"); +- + r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(1024ULL*1024ULL*16ULL)); + if (r < 0) + return log_warning_errno(r, "Setting RLIMIT_MEMLOCK failed, ignoring: %m"); +@@ -1651,6 +1629,8 @@ static void do_reexecute( + + static int invoke_main_loop( + Manager *m, ++ const struct rlimit *saved_rlimit_nofile, ++ const struct rlimit *saved_rlimit_memlock, + bool *ret_reexecute, + int *ret_retval, /* Return parameters relevant for shutting down */ + const char **ret_shutdown_verb, /* … */ +@@ -1662,6 +1642,8 @@ static int invoke_main_loop( + int r; + + assert(m); ++ assert(saved_rlimit_nofile); ++ assert(saved_rlimit_memlock); + assert(ret_reexecute); + assert(ret_retval); + assert(ret_shutdown_verb); +@@ -1691,7 +1673,7 @@ static int invoke_main_loop( + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + +- (void) parse_configuration(); ++ (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); + + set_manager_defaults(m); + +@@ -1983,6 +1965,80 @@ static int do_queue_default_job( + return 0; + } + ++static void save_rlimits(struct rlimit *saved_rlimit_nofile, ++ struct rlimit *saved_rlimit_memlock) { ++ ++ assert(saved_rlimit_nofile); ++ assert(saved_rlimit_memlock); ++ ++ if (getrlimit(RLIMIT_NOFILE, saved_rlimit_nofile) < 0) ++ log_warning_errno(errno, "Reading RLIMIT_NOFILE failed, ignoring: %m"); ++ ++ if (getrlimit(RLIMIT_MEMLOCK, saved_rlimit_memlock) < 0) ++ log_warning_errno(errno, "Reading RLIMIT_MEMLOCK failed, ignoring: %m"); ++} ++ ++static void fallback_rlimit_nofile(const struct rlimit *saved_rlimit_nofile) { ++ struct rlimit *rl; ++ ++ if (arg_default_rlimit[RLIMIT_NOFILE]) ++ return; ++ ++ /* Make sure forked processes get limits based on the original kernel setting */ ++ ++ rl = newdup(struct rlimit, saved_rlimit_nofile, 1); ++ if (!rl) { ++ log_oom(); ++ return; ++ } ++ ++ /* Bump the hard limit for system services to a substantially higher value. The default ++ * hard limit current kernels set is pretty low (4K), mostly for historical ++ * reasons. According to kernel developers, the fd handling in recent kernels has been ++ * optimized substantially enough, so that we can bump the limit now, without paying too ++ * high a price in memory or performance. Note however that we only bump the hard limit, ++ * not the soft limit. That's because select() works the way it works, and chokes on fds ++ * >= 1024. If we'd bump the soft limit globally, it might accidentally happen to ++ * unexpecting programs that they get fds higher than what they can process using ++ * select(). By only bumping the hard limit but leaving the low limit as it is we avoid ++ * this pitfall: programs that are written by folks aware of the select() problem in mind ++ * (and thus use poll()/epoll instead of select(), the way everybody should) can ++ * explicitly opt into high fds by bumping their soft limit beyond 1024, to the hard limit ++ * we pass. */ ++ if (arg_system) { ++ int nr; ++ ++ /* Get the underlying absolute limit the kernel enforces */ ++ nr = read_nr_open(); ++ ++ rl->rlim_max = MIN((rlim_t) nr, MAX(rl->rlim_max, (rlim_t) HIGH_RLIMIT_NOFILE)); ++ } ++ ++ /* If for some reason we were invoked with a soft limit above 1024 (which should never ++ * happen!, but who knows what we get passed in from pam_limit when invoked as --user ++ * instance), then lower what we pass on to not confuse our children */ ++ rl->rlim_cur = MIN(rl->rlim_cur, (rlim_t) FD_SETSIZE); ++ ++ arg_default_rlimit[RLIMIT_NOFILE] = rl; ++} ++ ++static void fallback_rlimit_memlock(const struct rlimit *saved_rlimit_memlock) { ++ struct rlimit *rl; ++ ++ /* Pass the original value down to invoked processes */ ++ ++ if (arg_default_rlimit[RLIMIT_MEMLOCK]) ++ return; ++ ++ rl = newdup(struct rlimit, saved_rlimit_memlock, 1); ++ if (!rl) { ++ log_oom(); ++ return; ++ } ++ ++ arg_default_rlimit[RLIMIT_MEMLOCK] = rl; ++} ++ + static void reset_arguments(void) { + /* Frees/resets arg_* variables, with a few exceptions commented below. */ + +@@ -2040,9 +2096,13 @@ static void reset_arguments(void) { + numa_policy_reset(&arg_numa_policy); + } + +-static int parse_configuration(void) { ++static int parse_configuration(const struct rlimit *saved_rlimit_nofile, ++ const struct rlimit *saved_rlimit_memlock) { + int r; + ++ assert(saved_rlimit_nofile); ++ assert(saved_rlimit_memlock); ++ + arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); + + /* Assign configuration defaults */ +@@ -2058,18 +2118,29 @@ static int parse_configuration(void) { + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + } + ++ /* Initialize some default rlimits for services if they haven't been configured */ ++ fallback_rlimit_nofile(saved_rlimit_nofile); ++ fallback_rlimit_memlock(saved_rlimit_memlock); ++ + /* Note that this also parses bits from the kernel command line, including "debug". */ + log_parse_environment(); + + return 0; + } + +-static int load_configuration(int argc, char **argv, const char **ret_error_message) { ++static int load_configuration( ++ int argc, ++ char **argv, ++ const struct rlimit *saved_rlimit_nofile, ++ const struct rlimit *saved_rlimit_memlock, ++ const char **ret_error_message) { + int r; + ++ assert(saved_rlimit_nofile); ++ assert(saved_rlimit_memlock); + assert(ret_error_message); + +- (void) parse_configuration(); ++ (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); + + r = parse_argv(argc, argv); + if (r < 0) { +@@ -2403,11 +2474,15 @@ int main(int argc, char *argv[]) { + } + } + ++ /* Save the original RLIMIT_NOFILE/RLIMIT_MEMLOCK so that we can reset it later when ++ * transitioning from the initrd to the main systemd or suchlike. */ ++ save_rlimits(&saved_rlimit_nofile, &saved_rlimit_memlock); ++ + /* Reset all signal handlers. */ + (void) reset_all_signal_handlers(); + (void) ignore_signals(SIGNALS_IGNORE, -1); + +- r = load_configuration(argc, argv, &error_message); ++ r = load_configuration(argc, argv, &saved_rlimit_nofile, &saved_rlimit_memlock, &error_message); + if (r < 0) + goto finish; + +@@ -2522,6 +2597,8 @@ int main(int argc, char *argv[]) { + } + + (void) invoke_main_loop(m, ++ &saved_rlimit_nofile, ++ &saved_rlimit_memlock, + &reexecute, + &retval, + &shutdown_verb, diff --git a/SOURCES/0322-main-introduce-a-define-HIGH_RLIMIT_MEMLOCK-similar-.patch b/SOURCES/0322-main-introduce-a-define-HIGH_RLIMIT_MEMLOCK-similar-.patch new file mode 100644 index 0000000..72ec11c --- /dev/null +++ b/SOURCES/0322-main-introduce-a-define-HIGH_RLIMIT_MEMLOCK-similar-.patch @@ -0,0 +1,37 @@ +From 0528a880ddf797a42b2de274e5c7bd2d9896c991 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 11 Oct 2018 18:31:11 +0200 +Subject: [PATCH] main: introduce a define HIGH_RLIMIT_MEMLOCK similar to + HIGH_RLIMIT_NOFILE + +(cherry picked from commit c8884aceefc85245b9bdfb626e2daf27521259bd) +Related: #1789930 +--- + src/basic/def.h | 3 +++ + src/core/main.c | 2 +- + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/basic/def.h b/src/basic/def.h +index 4d515c11b6..65ad659999 100644 +--- a/src/basic/def.h ++++ b/src/basic/def.h +@@ -75,3 +75,6 @@ + _CONF_PATHS_SPLIT_USR(n)) + + #define LONG_LINE_MAX (1U*1024U*1024U) ++ ++#define HIGH_RLIMIT_NOFILE (256*1024) ++#define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL) +diff --git a/src/core/main.c b/src/core/main.c +index b8c1e567ad..d6550ea161 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1168,7 +1168,7 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) { + * should normally disable such checks. We need them to implement IPAccessAllow= and IPAccessDeny=, hence let's + * bump the value high enough for the root user. */ + +- r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(1024ULL*1024ULL*16ULL)); ++ r = setrlimit_closest(RLIMIT_MEMLOCK, &RLIMIT_MAKE_CONST(HIGH_RLIMIT_MEMLOCK)); + if (r < 0) + return log_warning_errno(r, "Setting RLIMIT_MEMLOCK failed, ignoring: %m"); + diff --git a/SOURCES/0323-seccomp-introduce-seccomp_restrict_suid_sgid-for-blo.patch b/SOURCES/0323-seccomp-introduce-seccomp_restrict_suid_sgid-for-blo.patch new file mode 100644 index 0000000..d63169b --- /dev/null +++ b/SOURCES/0323-seccomp-introduce-seccomp_restrict_suid_sgid-for-blo.patch @@ -0,0 +1,178 @@ +From 5a62c0daff82e8343d24f98e1761d27bf8015782 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 20 Mar 2019 19:00:28 +0100 +Subject: [PATCH] seccomp: introduce seccomp_restrict_suid_sgid() for blocking + chmod() for suid/sgid files + +(cherry picked from commit 3c27973b13724ede05a06a5d346a569794cda433) +Related: #1687512 +--- + src/shared/seccomp-util.c | 132 ++++++++++++++++++++++++++++++++++++++ + src/shared/seccomp-util.h | 1 + + 2 files changed, 133 insertions(+) + +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index 92910acf0e..fd46b9f88d 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -1,12 +1,14 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + + #include ++#include + #include + #include + #include + #include + #include + #include ++#include + + #include "af-list.h" + #include "alloc-util.h" +@@ -1747,3 +1749,133 @@ int seccomp_lock_personality(unsigned long personality) { + + return 0; + } ++ ++int seccomp_restrict_suid_sgid(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; ++ ++ /* Checks the mode_t parameter of the following system calls: ++ * ++ * → chmod() + fchmod() + fchmodat() ++ * → open() + creat() + openat() ++ * → mkdir() + mkdirat() ++ * → mknod() + mknodat() ++ */ ++ ++ for (unsigned bit = 0; bit < 2; bit ++) { ++ /* Block S_ISUID in the first iteration, S_ISGID in the second */ ++ mode_t m = bit == 0 ? S_ISUID : S_ISGID; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ ++ 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) ++ break; ++ } ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add suid/sgid 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 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 d8a36c4e21..602f092255 100644 +--- a/src/shared/seccomp-util.h ++++ b/src/shared/seccomp-util.h +@@ -85,6 +85,7 @@ 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_restrict_suid_sgid(void); + + extern const uint32_t seccomp_local_archs[]; + diff --git a/SOURCES/0324-test-add-test-case-for-restrict_suid_sgid.patch b/SOURCES/0324-test-add-test-case-for-restrict_suid_sgid.patch new file mode 100644 index 0000000..c04084f --- /dev/null +++ b/SOURCES/0324-test-add-test-case-for-restrict_suid_sgid.patch @@ -0,0 +1,265 @@ +From b39697a80ad388e2063c54e56333882f4307c1a1 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 12 Nov 2019 13:27:49 +0100 +Subject: [PATCH] test: add test case for restrict_suid_sgid() + +(cherry picked from commit 167fc10cb352b04d442c9010dab4f8dc24219749) +Related: #1687512 +--- + src/test/test-seccomp.c | 226 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 226 insertions(+) + +diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c +index d177515ac7..4021a06e0e 100644 +--- a/src/test/test-seccomp.c ++++ b/src/test/test-seccomp.c +@@ -17,9 +17,11 @@ + #include "nsflags.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 "umask-util.h" + #include "util.h" + #include "virt.h" + +@@ -666,6 +668,229 @@ static void test_filter_sets_ordered(void) { + } + } + ++static int mkostemp_safe(char *pattern) { ++ _unused_ _cleanup_umask_ mode_t u = umask(0077); ++ int fd; ++ ++ assert(pattern); ++ ++ fd = mkostemp(pattern, O_CLOEXEC); ++ if (fd < 0) ++ return -errno; ++ ++ return fd; ++} ++ ++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[]) { + + log_set_max_level(LOG_DEBUG); +@@ -684,6 +909,7 @@ int main(int argc, char *argv[]) { + test_load_syscall_filter_set_raw(); + test_lock_personality(); + test_filter_sets_ordered(); ++ test_restrict_suid_sgid(); + + return 0; + } diff --git a/SOURCES/0325-core-expose-SUID-SGID-restriction-as-new-unit-settin.patch b/SOURCES/0325-core-expose-SUID-SGID-restriction-as-new-unit-settin.patch new file mode 100644 index 0000000..7d8d98a --- /dev/null +++ b/SOURCES/0325-core-expose-SUID-SGID-restriction-as-new-unit-settin.patch @@ -0,0 +1,157 @@ +From f79283a86531e3bbf0854b5f126b7b291fadfb43 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 20 Mar 2019 19:09:09 +0100 +Subject: [PATCH] core: expose SUID/SGID restriction as new unit setting + RestrictSUIDSGID= + +(cherry picked from commit f69567cbe26d09eac9d387c0be0fc32c65a83ada) +Related: #1687512 +--- + src/core/dbus-execute.c | 4 ++++ + src/core/execute.c | 22 +++++++++++++++++++++ + src/core/execute.h | 1 + + src/core/load-fragment-gperf.gperf.m4 | 2 ++ + src/shared/bus-unit-util.c | 2 +- + test/fuzz/fuzz-unit-file/directives.service | 1 + + 6 files changed, 31 insertions(+), 1 deletion(-) + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 198f149210..e7c0b893d1 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -815,6 +815,7 @@ 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), +@@ -1179,6 +1180,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); + +diff --git a/src/core/execute.c b/src/core/execute.c +index 56aa89e1ec..f012023224 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -1366,6 +1366,7 @@ 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 || +@@ -1470,6 +1471,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); +@@ -3404,6 +3418,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; +@@ -4023,6 +4043,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { + "%sIgnoreSIGPIPE: %s\n" + "%sMemoryDenyWriteExecute: %s\n" + "%sRestrictRealtime: %s\n" ++ "%sRestrictSUIDSGID: %s\n" + "%sKeyringMode: %s\n", + prefix, c->umask, + prefix, c->working_directory ? c->working_directory : "/", +@@ -4041,6 +4062,7 @@ 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, yes_no(c->restrict_suid_sgid), + prefix, exec_keyring_mode_to_string(c->keyring_mode)); + + if (c->root_image) +diff --git a/src/core/execute.h b/src/core/execute.h +index b2eb55f8f5..2266355962 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -245,6 +245,7 @@ struct ExecContext { + * that the autofs logic detects that it belongs to us and we + * don't enter a trigger loop. */ + bool same_pgrp; ++ bool restrict_suid_sgid; + + unsigned long personality; + bool lock_personality; +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index cdf4d14c4e..49e938d0ce 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -76,6 +76,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 +@@ -84,6 +85,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) +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 055edd6e22..3c42e97b7a 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -697,7 +697,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + "PrivateMounts", "NoNewPrivileges", "SyslogLevelPrefix", + "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", + "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", +- "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality")) ++ "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality" "RestrictSUIDSGID")) + + return bus_append_parse_boolean(m, field, eq); + +diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +index d8d1fc68b8..eab1820e20 100644 +--- a/test/fuzz/fuzz-unit-file/directives.service ++++ b/test/fuzz/fuzz-unit-file/directives.service +@@ -849,6 +849,7 @@ ReserveVT= + RestrictAddressFamilies= + RestrictNamespaces= + RestrictRealtime= ++RestrictSUIDSGID= + RuntimeDirectory= + RuntimeDirectoryMode= + RuntimeDirectoryPreserve= diff --git a/SOURCES/0326-analyze-check-for-RestrictSUIDSGID-in-systemd-analyz.patch b/SOURCES/0326-analyze-check-for-RestrictSUIDSGID-in-systemd-analyz.patch new file mode 100644 index 0000000..ffdc4bf --- /dev/null +++ b/SOURCES/0326-analyze-check-for-RestrictSUIDSGID-in-systemd-analyz.patch @@ -0,0 +1,52 @@ +From 3d338556760632b9c8b646a719d56e02e3ad2088 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 20 Mar 2019 19:20:35 +0100 +Subject: [PATCH] analyze: check for RestrictSUIDSGID= in "systemd-analyze + security" + +And let's give it a heigh weight, since it pretty much can be used for +bad things only. + +(cherry picked from commit 9d880b70ba5c6ca83c82952f4c90e86e56c7b70c) +Related: #1687512 +--- + src/analyze/analyze-security.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c +index eec040d5c3..969101c57b 100644 +--- a/src/analyze/analyze-security.c ++++ b/src/analyze/analyze-security.c +@@ -69,6 +69,7 @@ struct security_info { + + uint64_t restrict_namespaces; + bool restrict_realtime; ++ bool restrict_suid_sgid; + + char *root_directory; + char *root_image; +@@ -1130,6 +1131,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=", +@@ -1862,6 +1873,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/SOURCES/0327-man-document-the-new-RestrictSUIDSGID-setting.patch b/SOURCES/0327-man-document-the-new-RestrictSUIDSGID-setting.patch new file mode 100644 index 0000000..f362587 --- /dev/null +++ b/SOURCES/0327-man-document-the-new-RestrictSUIDSGID-setting.patch @@ -0,0 +1,83 @@ +From 797ebaa8240aefc39de3d1713468b221c83ed3f5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 20 Mar 2019 19:45:32 +0100 +Subject: [PATCH] man: document the new RestrictSUIDSGID= setting + +(cherry picked from commit 7445db6eb70e8d5989f481d0c5a08ace7047ae5b) +Related: #1687512 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.exec.xml | 41 +++++++++++++++++++++++++++------------ + 2 files changed, 30 insertions(+), 12 deletions(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 0ea444b133..c2b5c0dcce 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -149,6 +149,7 @@ All execution-related settings are available for transient units. + ✓ MemoryDenyWriteExecute= + ✓ RestrictNamespaces= + ✓ RestrictRealtime= ++✓ RestrictSUIDSGID= + ✓ RestrictAddressFamilies= + ✓ LockPersonality= + ✓ LimitCPU= +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index 87fb8b34f4..45ed1864f8 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -348,18 +348,19 @@ 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 ++ 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= 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. + + +@@ -1274,6 +1275,22 @@ 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). Defaults to off. ++ ++ + + RemoveIPC= + diff --git a/SOURCES/0328-units-turn-on-RestrictSUIDSGID-in-most-of-our-long-r.patch b/SOURCES/0328-units-turn-on-RestrictSUIDSGID-in-most-of-our-long-r.patch new file mode 100644 index 0000000..8a9917f --- /dev/null +++ b/SOURCES/0328-units-turn-on-RestrictSUIDSGID-in-most-of-our-long-r.patch @@ -0,0 +1,157 @@ +From b0573f1a6f8022aed4954d5ca19cc037d25cd5e7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 20 Mar 2019 19:52:20 +0100 +Subject: [PATCH] units: turn on RestrictSUIDSGID= in most of our long-running + daemons + +(cherry picked from commit 62aa29247c3d74bcec0607c347f2be23cd90675d) +Related: #1687512 +--- + units/systemd-coredump@.service.in | 1 + + units/systemd-hostnamed.service.in | 1 + + units/systemd-journal-remote.service.in | 1 + + units/systemd-journald.service.in | 1 + + units/systemd-localed.service.in | 1 + + units/systemd-logind.service.in | 1 + + units/systemd-networkd.service.in | 1 + + units/systemd-resolved.service.in | 1 + + units/systemd-timedated.service.in | 1 + + units/systemd-timesyncd.service.in | 1 + + units/systemd-udevd.service.in | 3 ++- + 11 files changed, 12 insertions(+), 1 deletion(-) + +diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in +index 68a68a5055..d69ebd8b24 100644 +--- a/units/systemd-coredump@.service.in ++++ b/units/systemd-coredump@.service.in +@@ -33,6 +33,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX ++RestrictSUIDSGID=yes + SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native +diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in +index 4e5470dd29..97d4e142bc 100644 +--- a/units/systemd-hostnamed.service.in ++++ b/units/systemd-hostnamed.service.in +@@ -29,6 +29,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX ++RestrictSUIDSGID=yes + SystemCallFilter=@system-service sethostname + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native +diff --git a/units/systemd-journal-remote.service.in b/units/systemd-journal-remote.service.in +index a94265f215..3c914f5a40 100644 +--- a/units/systemd-journal-remote.service.in ++++ b/units/systemd-journal-remote.service.in +@@ -28,6 +28,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 ++RestrictSUIDSGID=yes + SystemCallArchitectures=native + LockPersonality=yes + LogsDirectory=journal/remote +diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in +index e109b25792..ab9ec35ff8 100644 +--- a/units/systemd-journald.service.in ++++ b/units/systemd-journald.service.in +@@ -21,6 +21,7 @@ Sockets=systemd-journald.socket systemd-journald-dev-log.socket + ExecStart=@rootlibexecdir@/systemd-journald + Restart=always + RestartSec=0 ++RestrictSUIDSGID=yes + StandardOutput=null + WatchdogSec=3min + FileDescriptorStoreMax=4224 +diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in +index ce043db154..b87d60e9eb 100644 +--- a/units/systemd-localed.service.in ++++ b/units/systemd-localed.service.in +@@ -29,6 +29,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX ++RestrictSUIDSGID=yes + SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native +diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in +index 6953fac55b..086338e03b 100644 +--- a/units/systemd-logind.service.in ++++ b/units/systemd-logind.service.in +@@ -30,6 +30,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX AF_NETLINK ++RestrictSUIDSGID=yes + SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native +diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in +index 371ab3a9cf..a0f34ac738 100644 +--- a/units/systemd-networkd.service.in ++++ b/units/systemd-networkd.service.in +@@ -39,6 +39,7 @@ SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes ++RestrictSUIDSGID=yes + RuntimeDirectory=systemd/netif + RuntimeDirectoryPreserve=yes + +diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in +index aaed406ab2..6c2ad5ca86 100644 +--- a/units/systemd-resolved.service.in ++++ b/units/systemd-resolved.service.in +@@ -41,6 +41,7 @@ SystemCallFilter=@system-service + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes ++RestrictSUIDSGID=yes + RuntimeDirectory=systemd/resolve + RuntimeDirectoryPreserve=yes + +diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in +index 662b39557a..1da2bc4bb0 100644 +--- a/units/systemd-timedated.service.in ++++ b/units/systemd-timedated.service.in +@@ -27,6 +27,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX ++RestrictSUIDSGID=yes + SystemCallFilter=@system-service @clock + SystemCallErrorNumber=EPERM + SystemCallArchitectures=native +diff --git a/units/systemd-timesyncd.service.in b/units/systemd-timesyncd.service.in +index 4a490b6e16..c2b9551726 100644 +--- a/units/systemd-timesyncd.service.in ++++ b/units/systemd-timesyncd.service.in +@@ -37,6 +37,7 @@ MemoryDenyWriteExecute=yes + RestrictRealtime=yes + RestrictNamespaces=yes + RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 ++RestrictSUIDSGID=yes + RuntimeDirectory=systemd/timesync + SystemCallFilter=@system-service @clock + SystemCallErrorNumber=EPERM +diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in +index fd9ead3bb8..970cf0f290 100644 +--- a/units/systemd-udevd.service.in ++++ b/units/systemd-udevd.service.in +@@ -27,8 +27,9 @@ WatchdogSec=3min + TasksMax=infinity + PrivateMounts=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/SOURCES/0329-core-imply-NNP-and-SUID-SGID-restriction-for-Dynamic.patch b/SOURCES/0329-core-imply-NNP-and-SUID-SGID-restriction-for-Dynamic.patch new file mode 100644 index 0000000..df4279c --- /dev/null +++ b/SOURCES/0329-core-imply-NNP-and-SUID-SGID-restriction-for-Dynamic.patch @@ -0,0 +1,89 @@ +From 11f5677752f9b78239214b3064e5a2c3712d71b1 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 20 Mar 2019 20:19:38 +0100 +Subject: [PATCH] core: imply NNP and SUID/SGID restriction for DynamicUser=yes + service + +Let's be safe, rather than sorry. This way DynamicUser=yes services can +neither take benefit of, nor create SUID/SGID binaries. + +Given that DynamicUser= is a recent addition only we should be able to +get away with turning this on, even though this is strictly speaking a +binary compatibility breakage. + +(cherry picked from commit bf65b7e0c9fc215897b676ab9a7c9d1c688143ba) +Resolves: #1687512 +--- + man/systemd.exec.xml | 16 ++++++++++------ + src/core/unit.c | 10 ++++++++-- + 2 files changed, 18 insertions(+), 8 deletions(-) + +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index 45ed1864f8..bdaed68162 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -229,7 +229,9 @@ + 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 ++ 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 +@@ -357,11 +359,12 @@ CapabilityBoundingSet=~CAP_B CAP_C + RestrictAddressFamilies=, RestrictNamespaces=, + PrivateDevices=, ProtectKernelTunables=, + ProtectKernelModules=, MemoryDenyWriteExecute=, +- RestrictRealtime=, RestrictSUIDSGID= 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 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. ++ Flag. + + + +@@ -1288,7 +1291,8 @@ RestrictNamespaces=~cgroup net + 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). Defaults to off. ++ meaning than for files, see documentation). This option is implied if DynamicUser= ++ is enabled. Defaults to off. + + + +diff --git a/src/core/unit.c b/src/core/unit.c +index 115739f4c6..e1f5e6f7bd 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -4161,14 +4161,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; + } + } + diff --git a/SOURCES/0330-cgroup-introduce-support-for-cgroup-v2-CPUSET-contro.patch b/SOURCES/0330-cgroup-introduce-support-for-cgroup-v2-CPUSET-contro.patch new file mode 100644 index 0000000..d8d2f87 --- /dev/null +++ b/SOURCES/0330-cgroup-introduce-support-for-cgroup-v2-CPUSET-contro.patch @@ -0,0 +1,555 @@ +From b55c9b8e717d1967e6aa16c1e2646fc81d899ab7 Mon Sep 17 00:00:00 2001 +From: Pavel Hrdina +Date: Mon, 29 Jul 2019 17:50:05 +0200 +Subject: [PATCH] cgroup: introduce support for cgroup v2 CPUSET controller + +Introduce support for configuring cpus and mems for processes using +cgroup v2 CPUSET controller. This allows users to limit which cpus +and memory NUMA nodes can be used by processes to better utilize +system resources. + +The cgroup v2 interfaces to control it are cpuset.cpus and cpuset.mems +where the requested configuration is written. However, it doesn't mean +that the requested configuration will be actually used as parent cgroup +may limit the cpus or mems as well. In order to reflect the real +configuration cgroup v2 provides read-only files cpuset.cpus.effective +and cpuset.mems.effective which are exported to users as well. + +(cherry picked from commit 047f5d63d7a1ab75073f8485e2f9b550d25b0772) + +Related: #1724617 +--- + doc/TRANSIENT-SETTINGS.md | 2 + + man/systemd.resource-control.xml | 30 +++++++++++++ + src/basic/cgroup-util.c | 1 + + src/basic/cgroup-util.h | 2 + + src/core/cgroup.c | 63 +++++++++++++++++++++++++++ + src/core/cgroup.h | 5 +++ + src/core/dbus-cgroup.c | 59 +++++++++++++++++++++++++ + src/core/dbus-unit.c | 48 ++++++++++++++++++++ + src/core/load-fragment-gperf.gperf.m4 | 2 + + src/core/load-fragment.c | 38 ++++++++++++++++ + src/core/load-fragment.h | 2 + + src/shared/bus-unit-util.c | 16 +++++++ + src/systemctl/systemctl.c | 2 +- + src/test/test-cgroup-mask.c | 3 +- + 14 files changed, 271 insertions(+), 2 deletions(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index c2b5c0dcce..0b2ad66dcb 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -218,6 +218,8 @@ All cgroup/resource control settings are available for transient units + ✓ CPUShares= + ✓ StartupCPUShares= + ✓ CPUQuota= ++✓ AllowedCPUs= ++✓ AllowedMemoryNodes= + ✓ MemoryAccounting= + ✓ MemoryLow= + ✓ MemoryHigh= +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index 370c110592..4329742e94 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -201,6 +201,36 @@ + + + ++ ++ AllowedCPUs= ++ ++ ++ Restrict processes to be executed on specific CPUs. Takes a list of CPU indices or ranges separated by either ++ whitespace or commas. CPU ranges are specified by the lower and upper CPU indices separated by a dash. ++ ++ Setting AllowedCPUs= doesn't guarantee that all of the CPUs will be used by the processes ++ as it may be limited by parent units. The effective configuration is reported as EffectiveCPUs=. ++ ++ This setting is supported only with the unified control group hierarchy. ++ ++ ++ ++ ++ AllowedMemoryNodes= ++ ++ ++ Restrict processes to be executed on specific memory NUMA nodes. Takes a list of memory NUMA nodes indices ++ or ranges separated by either whitespace or commas. Memory NUMA nodes ranges are specified by the lower and upper ++ CPU indices separated by a dash. ++ ++ Setting AllowedMemoryNodes= doesn't guarantee that all of the memory NUMA nodes will ++ be used by the processes as it may be limited by parent units. The effective configuration is reported as ++ EffectiveMemoryNodes=. ++ ++ This setting is supported only with the unified control group hierarchy. ++ ++ ++ + + MemoryAccounting= + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index 038ece4b06..6f47c3aacb 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -2763,6 +2763,7 @@ bool fd_is_cgroup_fs(int fd) { + static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { + [CGROUP_CONTROLLER_CPU] = "cpu", + [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", ++ [CGROUP_CONTROLLER_CPUSET] = "cpuset", + [CGROUP_CONTROLLER_IO] = "io", + [CGROUP_CONTROLLER_BLKIO] = "blkio", + [CGROUP_CONTROLLER_MEMORY] = "memory", +diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h +index 26e3ae0404..b414600dca 100644 +--- a/src/basic/cgroup-util.h ++++ b/src/basic/cgroup-util.h +@@ -21,6 +21,7 @@ + typedef enum CGroupController { + CGROUP_CONTROLLER_CPU, + CGROUP_CONTROLLER_CPUACCT, /* v1 only */ ++ CGROUP_CONTROLLER_CPUSET, /* v2 only */ + CGROUP_CONTROLLER_IO, /* v2 only */ + CGROUP_CONTROLLER_BLKIO, /* v1 only */ + CGROUP_CONTROLLER_MEMORY, +@@ -36,6 +37,7 @@ typedef enum CGroupController { + typedef enum CGroupMask { + CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), + CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), ++ CGROUP_MASK_CPUSET = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUSET), + CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), + CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), + CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 76eafdc082..664d269483 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -161,9 +161,14 @@ void cgroup_context_done(CGroupContext *c) { + + c->ip_address_allow = ip_address_access_free_all(c->ip_address_allow); + c->ip_address_deny = ip_address_access_free_all(c->ip_address_deny); ++ ++ cpu_set_reset(&c->cpuset_cpus); ++ cpu_set_reset(&c->cpuset_mems); + } + + void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { ++ _cleanup_free_ char *cpuset_cpus = NULL; ++ _cleanup_free_ char *cpuset_mems = NULL; + CGroupIODeviceLimit *il; + CGroupIODeviceWeight *iw; + CGroupBlockIODeviceBandwidth *b; +@@ -177,6 +182,9 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + + prefix = strempty(prefix); + ++ cpuset_cpus = cpu_set_to_range_string(&c->cpuset_cpus); ++ cpuset_mems = cpu_set_to_range_string(&c->cpuset_mems); ++ + fprintf(f, + "%sCPUAccounting=%s\n" + "%sIOAccounting=%s\n" +@@ -189,6 +197,8 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + "%sCPUShares=%" PRIu64 "\n" + "%sStartupCPUShares=%" PRIu64 "\n" + "%sCPUQuotaPerSecSec=%s\n" ++ "%sAllowedCPUs=%s\n" ++ "%sAllowedMemoryNodes=%s\n" + "%sIOWeight=%" PRIu64 "\n" + "%sStartupIOWeight=%" PRIu64 "\n" + "%sBlockIOWeight=%" PRIu64 "\n" +@@ -212,6 +222,8 @@ 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, cpuset_cpus, ++ prefix, cpuset_mems, + prefix, c->io_weight, + prefix, c->startup_io_weight, + prefix, c->blockio_weight, +@@ -541,6 +553,21 @@ static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) { + CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX); + } + ++static void cgroup_apply_unified_cpuset(Unit *u, CPUSet cpus, const char *name) { ++ _cleanup_free_ char *buf = NULL; ++ int r; ++ ++ buf = cpu_set_to_range_string(&cpus); ++ if (!buf) ++ return; ++ ++ r = cg_set_attribute("cpuset", u->cgroup_path, name, buf); ++ if (r < 0) ++ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, ++ "Failed to set %s: %m", name); ++ ++} ++ + static bool cgroup_context_has_io_config(CGroupContext *c) { + return c->io_accounting || + c->io_weight != CGROUP_WEIGHT_INVALID || +@@ -766,6 +793,11 @@ static void cgroup_context_apply( + } + } + ++ if ((apply_mask & CGROUP_MASK_CPUSET) && !is_root) { ++ cgroup_apply_unified_cpuset(u, c->cpuset_cpus, "cpuset.cpus"); ++ cgroup_apply_unified_cpuset(u, c->cpuset_mems, "cpuset.mems"); ++ } ++ + if (apply_mask & CGROUP_MASK_IO) { + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); +@@ -1068,6 +1100,9 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { + c->cpu_quota_per_sec_usec != USEC_INFINITY) + mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; + ++ if (c->cpuset_cpus.set || c->cpuset_mems.set) ++ mask |= CGROUP_MASK_CPUSET; ++ + if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c)) + mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; + +@@ -2697,4 +2732,32 @@ static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = + [CGROUP_STRICT] = "strict", + }; + ++int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) { ++ _cleanup_free_ char *v = NULL; ++ int r; ++ ++ assert(u); ++ assert(cpus); ++ ++ if (!u->cgroup_path) ++ return -ENODATA; ++ ++ if ((u->cgroup_realized_mask & CGROUP_MASK_CPUSET) == 0) ++ return -ENODATA; ++ ++ r = cg_all_unified(); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return -ENODATA; ++ if (r > 0) ++ r = cg_get_attribute("cpuset", u->cgroup_path, name, &v); ++ if (r == -ENOENT) ++ return -ENODATA; ++ if (r < 0) ++ return r; ++ ++ return parse_cpu_set_full(v, cpus, false, NULL, NULL, 0, NULL); ++} ++ + DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy); +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index 2d2ff6fc3c..da10575394 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -4,6 +4,7 @@ + #include + + #include "cgroup-util.h" ++#include "cpu-set-util.h" + #include "ip-address-access.h" + #include "list.h" + #include "time-util.h" +@@ -77,6 +78,9 @@ struct CGroupContext { + uint64_t startup_cpu_weight; + usec_t cpu_quota_per_sec_usec; + ++ CPUSet cpuset_cpus; ++ CPUSet cpuset_mems; ++ + uint64_t io_weight; + uint64_t startup_io_weight; + LIST_HEAD(CGroupIODeviceWeight, io_device_weights); +@@ -205,3 +209,4 @@ const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; + CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; + + bool unit_cgroup_delegate(Unit *u); ++int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name); +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 540bc77aed..30d4e83932 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -53,6 +53,27 @@ static int property_get_delegate_controllers( + return sd_bus_message_close_container(reply); + } + ++static int property_get_cpuset( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ CPUSet *cpus = userdata; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ ++ assert(bus); ++ assert(reply); ++ assert(cpus); ++ ++ (void) cpu_set_to_dbus(cpus, &array, &allocated); ++ return sd_bus_message_append_array(reply, 'y', array, allocated); ++} ++ + static int property_get_io_device_weight( + sd_bus *bus, + const char *path, +@@ -283,6 +304,8 @@ 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("AllowedCPUs", "ay", property_get_cpuset, offsetof(CGroupContext, cpuset_cpus), 0), ++ SD_BUS_PROPERTY("AllowedMemoryNodes", "ay", property_get_cpuset, offsetof(CGroupContext, cpuset_mems), 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), +@@ -671,6 +694,42 @@ int bus_cgroup_set_property( + + return 1; + ++ } else if (STR_IN_SET(name, "AllowedCPUs", "AllowedMemoryNodes")) { ++ const void *a; ++ size_t n; ++ _cleanup_(cpu_set_reset) CPUSet new_set = {}; ++ ++ r = sd_bus_message_read_array(message, 'y', &a, &n); ++ if (r < 0) ++ return r; ++ ++ r = cpu_set_from_dbus(a, n, &new_set); ++ if (r < 0) ++ return r; ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ _cleanup_free_ char *setstr = NULL; ++ _cleanup_free_ char *data = NULL; ++ CPUSet *set; ++ ++ setstr = cpu_set_to_range_string(&new_set); ++ ++ if (streq(name, "AllowedCPUs")) ++ set = &c->cpuset_cpus; ++ else ++ set = &c->cpuset_mems; ++ ++ if (asprintf(&data, "%s=%s", name, setstr) < 0) ++ return -ENOMEM; ++ ++ cpu_set_reset(set); ++ cpu_set_add_all(set, &new_set); ++ unit_invalidate_cgroup(u, CGROUP_MASK_CPUSET); ++ unit_write_setting(u, flags, name, data); ++ } ++ ++ 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-unit.c b/src/core/dbus-unit.c +index c5bca10979..aa15e47754 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -752,6 +752,52 @@ static int property_get_cpu_usage( + return sd_bus_message_append(reply, "t", ns); + } + ++static int property_get_cpuset_cpus( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ Unit *u = userdata; ++ _cleanup_(cpu_set_reset) CPUSet cpus = {}; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ ++ assert(bus); ++ assert(reply); ++ assert(u); ++ ++ (void) unit_get_cpuset(u, &cpus, "cpuset.cpus.effective"); ++ (void) cpu_set_to_dbus(&cpus, &array, &allocated); ++ return sd_bus_message_append_array(reply, 'y', array, allocated); ++} ++ ++static int property_get_cpuset_mems( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ Unit *u = userdata; ++ _cleanup_(cpu_set_reset) CPUSet mems = {}; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ ++ assert(bus); ++ assert(reply); ++ assert(u); ++ ++ (void) unit_get_cpuset(u, &mems, "cpuset.mems.effective"); ++ (void) cpu_set_to_dbus(&mems, &array, &allocated); ++ return sd_bus_message_append_array(reply, 'y', array, allocated); ++} ++ + static int property_get_cgroup( + sd_bus *bus, + const char *path, +@@ -1074,6 +1120,8 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { + SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), + SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), + SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), ++ SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0), ++ SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0), + SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), + SD_BUS_PROPERTY("IPIngressBytes", "t", property_get_ip_counter, 0, 0), + SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0), +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 49e938d0ce..ebb44df487 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -167,6 +167,8 @@ $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.CPUSetCpus, config_parse_cpuset_cpus, 0, offsetof($1, cgroup_context) ++$1.CPUSetMems, config_parse_cpuset_mems, 0, offsetof($1, cgroup_context) + $1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting) + $1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 35dd595098..6debf82401 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3011,6 +3011,44 @@ int config_parse_cpu_quota( + return 0; + } + ++int config_parse_cpuset_cpus( ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *section, ++ unsigned section_line, ++ const char *lvalue, ++ int ltype, ++ const char *rvalue, ++ void *data, ++ void *userdata) { ++ ++ CGroupContext *c = data; ++ ++ (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue); ++ ++ return 0; ++} ++ ++int config_parse_cpuset_mems( ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *section, ++ unsigned section_line, ++ const char *lvalue, ++ int ltype, ++ const char *rvalue, ++ void *data, ++ void *userdata) { ++ ++ CGroupContext *c = data; ++ ++ (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue); ++ ++ return 0; ++} ++ + int config_parse_memory_limit( + const char *unit, + const char *filename, +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index f2ca1b8ee7..6612e1fb32 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -86,6 +86,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_set_status); + CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv); + CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems); + CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota); ++CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_cpus); ++CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_mems); + CONFIG_PARSER_PROTOTYPE(config_parse_protect_home); + CONFIG_PARSER_PROTOTYPE(config_parse_protect_system); + CONFIG_PARSER_PROTOTYPE(config_parse_bus_name); +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 3c42e97b7a..8f3b463c6b 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -396,6 +396,22 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons + + return bus_append_cg_cpu_shares_parse(m, field, eq); + ++ if (STR_IN_SET(field, "AllowedCPUs", "AllowedMemoryNodes")) { ++ _cleanup_(cpu_set_reset) CPUSet cpuset = {}; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; ++ ++ r = parse_cpu_set(eq, &cpuset); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse %s value: %s", field, eq); ++ ++ r = cpu_set_to_dbus(&cpuset, &array, &allocated); ++ if (r < 0) ++ return log_error_errno(r, "Failed to serialize CPUSet: %m"); ++ ++ return bus_append_byte_array(m, field, array, allocated); ++ } ++ + if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) + + return bus_append_cg_blkio_weight_parse(m, field, eq); +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 7274921e6d..a3074bc5e3 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -4892,7 +4892,7 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool + print_prop(name, "%s", h); + + return 1; +- } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "CPUAffinity", "NUMAMask")) { ++ } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "CPUAffinity", "NUMAMask", "AllowedCPUs", "AllowedMemoryNodes", "EffectiveCPUs", "EffectiveMemoryNodes")) { + _cleanup_free_ char *affinity = NULL; + _cleanup_(cpu_set_reset) CPUSet set = {}; + const void *a; +diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c +index d65959edf1..93c3f5d856 100644 +--- a/src/test/test-cgroup-mask.c ++++ b/src/test/test-cgroup-mask.c +@@ -104,9 +104,10 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) { + + static void test_cg_mask_to_string(void) { + test_cg_mask_to_string_one(0, NULL); +- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct io blkio memory devices pids"); ++ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids"); + test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu"); + test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct"); ++ test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset"); + test_cg_mask_to_string_one(CGROUP_MASK_IO, "io"); + test_cg_mask_to_string_one(CGROUP_MASK_BLKIO, "blkio"); + test_cg_mask_to_string_one(CGROUP_MASK_MEMORY, "memory"); diff --git a/SOURCES/0331-pid1-fix-DefaultTasksMax-initialization.patch b/SOURCES/0331-pid1-fix-DefaultTasksMax-initialization.patch new file mode 100644 index 0000000..0a146a3 --- /dev/null +++ b/SOURCES/0331-pid1-fix-DefaultTasksMax-initialization.patch @@ -0,0 +1,38 @@ +From e809564cfa5af01a26075682d49f81a987c41dd8 Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Wed, 2 Oct 2019 11:58:16 +0200 +Subject: [PATCH] pid1: fix DefaultTasksMax initialization + +Otherwise DefaultTasksMax is always set to "inifinity". + +This was broken by fb39af4ce42. + +(cherry picked from commit c0000de87d2c7934cb1f4ba66a533a85277600ff) + +Resolves: #1809037 +--- + src/core/main.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index d6550ea161..45d09b1e11 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -2088,7 +2088,7 @@ static void reset_arguments(void) { + arg_default_blockio_accounting = false; + arg_default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT; + arg_default_tasks_accounting = true; +- arg_default_tasks_max = UINT64_MAX; ++ arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); + arg_machine_id = (sd_id128_t) {}; + arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; + +@@ -2103,8 +2103,6 @@ static int parse_configuration(const struct rlimit *saved_rlimit_nofile, + assert(saved_rlimit_nofile); + assert(saved_rlimit_memlock); + +- arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); +- + /* Assign configuration defaults */ + reset_arguments(); + diff --git a/SOURCES/0332-cgroup-make-sure-that-cpuset-is-supported-on-cgroup-.patch b/SOURCES/0332-cgroup-make-sure-that-cpuset-is-supported-on-cgroup-.patch new file mode 100644 index 0000000..d2c0d2e --- /dev/null +++ b/SOURCES/0332-cgroup-make-sure-that-cpuset-is-supported-on-cgroup-.patch @@ -0,0 +1,40 @@ +From 5fc2d94fbf8271bb340e834f832af5d890c267bf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Tue, 3 Mar 2020 11:45:00 +0100 +Subject: [PATCH] cgroup: make sure that cpuset is supported on cgroup v2 and + disabled with v1 + +Resolves: #1808940 + +(rhel-only) +--- + src/basic/cgroup-util.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index 6f47c3aacb..92bc1f2543 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -2353,10 +2353,10 @@ int cg_mask_supported(CGroupMask *ret) { + if (r < 0) + return r; + +- /* Currently, we support the cpu, memory, io and pids ++ /* Currently, we support the cpu, memory, io, pids and cpuset + * controller in the unified hierarchy, mask + * everything else off. */ +- mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS; ++ mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS | CGROUP_MASK_CPUSET; + + } else { + CGroupController c; +@@ -2367,6 +2367,9 @@ int cg_mask_supported(CGroupMask *ret) { + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + const char *n; + ++ if (c == CGROUP_CONTROLLER_CPUSET) ++ continue; ++ + n = cgroup_controller_to_string(c); + if (controller_is_accessible(n) >= 0) + mask |= CGROUP_CONTROLLER_TO_MASK(c); diff --git a/SOURCES/0333-test-introduce-TEST-36-NUMAPOLICY.patch b/SOURCES/0333-test-introduce-TEST-36-NUMAPOLICY.patch new file mode 100644 index 0000000..811b91e --- /dev/null +++ b/SOURCES/0333-test-introduce-TEST-36-NUMAPOLICY.patch @@ -0,0 +1,380 @@ +From 90dda340e4adeb1126639a849d4f31ae327fdc4b Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 25 Jun 2019 23:01:40 +0200 +Subject: [PATCH] test: introduce TEST-36-NUMAPOLICY + +(cherry picked from commit 8f65e26508969610ac934d1aadbade8223bfcaac) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/Makefile | 1 + + test/TEST-36-NUMAPOLICY/test.sh | 51 +++++ + test/TEST-36-NUMAPOLICY/testsuite.sh | 292 +++++++++++++++++++++++++++ + 3 files changed, 344 insertions(+) + create mode 120000 test/TEST-36-NUMAPOLICY/Makefile + create mode 100755 test/TEST-36-NUMAPOLICY/test.sh + create mode 100755 test/TEST-36-NUMAPOLICY/testsuite.sh + +diff --git a/test/TEST-36-NUMAPOLICY/Makefile b/test/TEST-36-NUMAPOLICY/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-36-NUMAPOLICY/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh +new file mode 100755 +index 0000000000..a0d8623e8e +--- /dev/null ++++ b/test/TEST-36-NUMAPOLICY/test.sh +@@ -0,0 +1,51 @@ ++#!/bin/bash ++set -e ++TEST_DESCRIPTION="test MUMAPolicy= and NUMAMask= options" ++TEST_NO_NSPAWN=1 ++QEMU_OPTIONS="-numa node,nodeid=0" ++ ++. $TEST_BASE_DIR/test-functions ++ ++test_setup() { ++ create_empty_image ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ ++ ( ++ LOG_LEVEL=5 ++ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) ++ ++ setup_basic_environment ++ inst_binary mktemp ++ ++ # mask some services that we do not want to run in these tests ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service < $journalLog ++} ++ ++stopJournalctl() { ++ # Wait a few seconds until the messages get properly queued... ++ sleep $journalSleep ++ # ...and then force journald to write them to the backing storage ++ # Also, using journalctl --sync should be better than using SIGRTMIN+1, as ++ # the --sync wait until the synchronization is complete ++ echo "Force journald to write all queued messages" ++ journalctl --sync ++ kill -s TERM $COPROC_PID ++} ++ ++checkNUMA() { ++ # NUMA enabled system should have at least NUMA node0 ++ test -e /sys/devices/system/node/node0 ++} ++ ++writePID1NUMAPolicy() { ++ echo [Manager] > $confDir/numa.conf ++ echo NUMAPolicy=$1 >> $confDir/numa.conf ++ echo NUMAMask=$2>> $confDir/numa.conf ++} ++ ++writeTestUnit() { ++ echo [Service] > $testUnitFile ++ echo ExecStart=/bin/sleep 3600 >> $testUnitFile ++ mkdir -p $testUnitFile.d/ ++} ++ ++writeTestUnitNUMAPolicy() { ++ echo [Service] > $testUnitNUMAConf ++ echo NUMAPolicy=$1 >> $testUnitNUMAConf ++ echo NUMAMask=$2>> $testUnitNUMAConf ++ systemctl daemon-reload ++} ++ ++pid1ReloadWithStrace() { ++ startStrace ++ systemctl daemon-reload ++ stopStrace ++} ++ ++pid1ReloadWithJournal() { ++ startJournalctl ++ systemctl daemon-reload ++ stopJournalctl ++} ++ ++pid1StartUnitWithStrace() { ++ startStrace '-f' ++ systemctl start $1 ++ sleep $sleepAfterStart ++ stopStrace ++} ++ ++pid1StartUnitWithJournal() { ++ startJournalctl ++ systemctl start $1 ++ sleep $sleepAfterStart ++ stopJournalctl ++} ++ ++pid1StopUnit() { ++ systemctl stop $1 ++} ++ ++systemctlCheckNUMAProperties() { ++ local LOGFILE="$(mktemp)" ++ systemctl show -p NUMAPolicy $1 > "$LOGFILE" ++ grep "NUMAPolicy=$2" "$LOGFILE" ++ ++ > "$LOGFILE" ++ ++ if [ -n $3 ]; then ++ systemctl show -p NUMAMask $1 > "$LOGFILE" ++ grep "NUMAMask=$3" "$LOGFILE" ++ fi ++} ++ ++checkNUMA ++writeTestUnit ++ ++# Create systemd config drop-in directory ++confDir="/etc/systemd/system.conf.d/" ++mkdir -p "$confDir" ++ ++echo "PID1 NUMAPolicy support - Default policy w/o mask" ++writePID1NUMAPolicy "default" ++pid1ReloadWithStrace ++# Kernel requires that nodemask argument is set to NULL when setting default policy ++grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++echo "PID1 NUMAPolicy support - Default policy w/ mask" ++writePID1NUMAPolicy "default" "0" ++pid1ReloadWithStrace ++grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++echo "PID1 NUMAPolicy support - Bind policy w/o mask" ++writePID1NUMAPolicy "bind" ++pid1ReloadWithJournal ++grep "Failed to set NUMA memory policy: Invalid argument" $journalLog ++ ++echo "PID1 NUMAPolicy support - Bind policy w/ mask" ++writePID1NUMAPolicy "bind" "0" ++pid1ReloadWithStrace ++grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog ++ ++echo "PID1 NUMAPolicy support - Interleave policy w/o mask" ++writePID1NUMAPolicy "interleave" ++pid1ReloadWithJournal ++grep "Failed to set NUMA memory policy: Invalid argument" $journalLog ++ ++echo "PID1 NUMAPolicy support - Interleave policy w/ mask" ++writePID1NUMAPolicy "interleave" "0" ++pid1ReloadWithStrace ++grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog ++ ++echo "PID1 NUMAPolicy support - Preferred policy w/o mask" ++writePID1NUMAPolicy "preferred" ++pid1ReloadWithJournal ++# Preferred policy with empty node mask is actually allowed and should reset allocation policy to default ++! grep "Failed to set NUMA memory policy: Invalid argument" $journalLog ++ ++echo "PID1 NUMAPolicy support - Preferred policy w/ mask" ++writePID1NUMAPolicy "preferred" "0" ++pid1ReloadWithStrace ++grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog ++ ++echo "PID1 NUMAPolicy support - Local policy w/o mask" ++writePID1NUMAPolicy "local" ++pid1ReloadWithStrace ++# Kernel requires that nodemask argument is set to NULL when setting default policy ++grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++ ++echo "PID1 NUMAPolicy support - Local policy w/ mask" ++writePID1NUMAPolicy "local" "0" ++pid1ReloadWithStrace ++grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++ ++echo "Unit file NUMAPolicy support - Default policy w/o mask" ++writeTestUnitNUMAPolicy "default" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "default" ++pid1StopUnit $testUnit ++grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++echo "Unit file NUMAPolicy support - Default policy w/ mask" ++writeTestUnitNUMAPolicy "default" "0" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "default" "0" ++pid1StopUnit $testUnit ++# Maks must be ignored ++grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++echo "Unit file NUMAPolicy support - Bind policy w/o mask" ++writeTestUnitNUMAPolicy "bind" ++pid1StartUnitWithJournal $testUnit ++pid1StopUnit $testUnit ++grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog ++ ++echo "Unit file NUMAPolicy support - Bind policy w/ mask" ++writeTestUnitNUMAPolicy "bind" "0" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "bind" "0" ++pid1StopUnit $testUnit ++grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog ++ ++echo "Unit file NUMAPolicy support - Interleave policy w/o mask" ++writeTestUnitNUMAPolicy "interleave" ++pid1StartUnitWithStrace $testUnit ++pid1StopUnit $testUnit ++grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog ++ ++echo "Unit file NUMAPolicy support - Interleave policy w/ mask" ++writeTestUnitNUMAPolicy "interleave" "0" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "interleave" "0" ++pid1StopUnit $testUnit ++grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog ++ ++echo "Unit file NUMAPolicy support - Preferred policy w/o mask" ++writeTestUnitNUMAPolicy "preferred" ++pid1StartUnitWithJournal $testUnit ++systemctlCheckNUMAProperties $testUnit "preferred" ++pid1StopUnit $testUnit ++! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog ++ ++echo "Unit file NUMAPolicy support - Preferred policy w/ mask" ++writeTestUnitNUMAPolicy "preferred" "0" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "preferred" "0" ++pid1StopUnit $testUnit ++grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog ++ ++echo "Unit file NUMAPolicy support - Local policy w/o mask" ++writeTestUnitNUMAPolicy "local" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "local" ++pid1StopUnit $testUnit ++grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++ ++echo "Unit file NUMAPolicy support - Local policy w/ mask" ++writeTestUnitNUMAPolicy "local" "0" ++pid1StartUnitWithStrace $testUnit ++systemctlCheckNUMAProperties $testUnit "local" "0" ++pid1StopUnit $testUnit ++# Maks must be ignored ++grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++ ++echo "systemd-run NUMAPolicy support" ++runUnit='numa-systemd-run-test.service' ++ ++systemd-run -p NUMAPolicy=default --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "default" ++pid1StopUnit $runUnit ++ ++systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "default" "" ++pid1StopUnit $runUnit ++ ++systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "bind" "0" ++pid1StopUnit $runUnit ++ ++systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "interleave" "0" ++pid1StopUnit $runUnit ++ ++systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "preferred" "0" ++pid1StopUnit $runUnit ++ ++systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "local" ++pid1StopUnit $runUnit ++ ++systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000 ++systemctlCheckNUMAProperties $runUnit "local" "" ++pid1StopUnit $runUnit ++ ++# Cleanup ++rm -rf $testDir ++rm -rf $confDir ++systemctl daemon-reload ++ ++systemd-analyze log-level info ++ ++echo OK > /testok ++ ++exit 0 diff --git a/SOURCES/0334-test-replace-tail-f-with-journal-cursor-which-should.patch b/SOURCES/0334-test-replace-tail-f-with-journal-cursor-which-should.patch new file mode 100644 index 0000000..afe81ee --- /dev/null +++ b/SOURCES/0334-test-replace-tail-f-with-journal-cursor-which-should.patch @@ -0,0 +1,52 @@ +From b93a2617d49d9636801130d974995cabe6335b71 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 1 Jul 2019 09:27:59 +0200 +Subject: [PATCH] test: replace `tail -f` with journal cursor which should + be... + +more reliable + +(cherry picked from commit d0b2178f3e79f302702bd7140766eee03643f734) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/testsuite.sh | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index e15087b137..306a96b517 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -29,6 +29,9 @@ testUnitNUMAConf="$testUnitFile.d/numa.conf" + journalSleep=5 + sleepAfterStart=1 + ++# Journal cursor for easier navigation ++journalCursorFile="jounalCursorFile" ++ + startStrace() { + coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1 + } +@@ -38,18 +41,16 @@ stopStrace() { + } + + startJournalctl() { +- coproc journalctl -u init.scope -f > $journalLog ++ # Save journal's cursor for later navigation ++ journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat + } + + stopJournalctl() { +- # Wait a few seconds until the messages get properly queued... +- sleep $journalSleep +- # ...and then force journald to write them to the backing storage +- # Also, using journalctl --sync should be better than using SIGRTMIN+1, as ++ # Using journalctl --sync should be better than using SIGRTMIN+1, as + # the --sync wait until the synchronization is complete + echo "Force journald to write all queued messages" + journalctl --sync +- kill -s TERM $COPROC_PID ++ journalctl -u init.scope --cursor-file="$journalCursorFile" > "$journalLog" + } + + checkNUMA() { diff --git a/SOURCES/0335-test-support-MPOL_LOCAL-matching-in-unpatched-strace.patch b/SOURCES/0335-test-support-MPOL_LOCAL-matching-in-unpatched-strace.patch new file mode 100644 index 0000000..88a2e32 --- /dev/null +++ b/SOURCES/0335-test-support-MPOL_LOCAL-matching-in-unpatched-strace.patch @@ -0,0 +1,58 @@ +From d6d43b81df76d571d57f83ceb050c8b4ac4701b8 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 1 Jul 2019 13:08:26 +0200 +Subject: [PATCH] test: support MPOL_LOCAL matching in unpatched strace + versions + +The MPOL_LOCAL constant is not recognized in current strace versions. +Let's match at least the numerical value of this constant until the +strace patch is approved & merged. + +(cherry picked from commit ac14396d027023e1be910327989cb422cb2f6724) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/testsuite.sh | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index 306a96b517..a4134bdeca 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -173,12 +173,16 @@ echo "PID1 NUMAPolicy support - Local policy w/o mask" + writePID1NUMAPolicy "local" + pid1ReloadWithStrace + # Kernel requires that nodemask argument is set to NULL when setting default policy +-grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++# The unpatched versions of strace don't recognize the MPOL_LOCAL constant and ++# return a numerical constant instead (with a comment): ++# set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0 ++# Let's cover this scenario as well ++grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "PID1 NUMAPolicy support - Local policy w/ mask" + writePID1NUMAPolicy "local" "0" + pid1ReloadWithStrace +-grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "Unit file NUMAPolicy support - Default policy w/o mask" + writeTestUnitNUMAPolicy "default" +@@ -240,7 +244,7 @@ writeTestUnitNUMAPolicy "local" + pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "local" + pid1StopUnit $testUnit +-grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "Unit file NUMAPolicy support - Local policy w/ mask" + writeTestUnitNUMAPolicy "local" "0" +@@ -248,7 +252,7 @@ pid1StartUnitWithStrace $testUnit + systemctlCheckNUMAProperties $testUnit "local" "0" + pid1StopUnit $testUnit + # Maks must be ignored +-grep "set_mempolicy(MPOL_LOCAL, NULL" $straceLog ++grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + + echo "systemd-run NUMAPolicy support" + runUnit='numa-systemd-run-test.service' diff --git a/SOURCES/0336-test-make-sure-the-strace-process-is-indeed-dead.patch b/SOURCES/0336-test-make-sure-the-strace-process-is-indeed-dead.patch new file mode 100644 index 0000000..24d38b3 --- /dev/null +++ b/SOURCES/0336-test-make-sure-the-strace-process-is-indeed-dead.patch @@ -0,0 +1,50 @@ +From 60813b55f9b5b44b14f38bbc1b8c0d2b30e3f6c7 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 1 Jul 2019 19:53:45 +0200 +Subject: [PATCH] test: make sure the strace process is indeed dead + +It may take a few moments for the strace process to properly terminate +and write all logs to the backing storage + +(cherry picked from commit 56425e54a2140f47b4560b51c5db08aa2de199a6) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/test.sh | 2 +- + test/TEST-36-NUMAPOLICY/testsuite.sh | 3 +++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh +index a0d8623e8e..f0a321e7a1 100755 +--- a/test/TEST-36-NUMAPOLICY/test.sh ++++ b/test/TEST-36-NUMAPOLICY/test.sh +@@ -16,7 +16,7 @@ test_setup() { + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment +- inst_binary mktemp ++ dracut_install mktemp + + # mask some services that we do not want to run in these tests + ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index a4134bdeca..daed8fcc1c 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -38,6 +38,8 @@ startStrace() { + + stopStrace() { + kill -s TERM $COPROC_PID ++ # Make sure the strace process is indeed dead ++ while kill -0 $COPROC_PID 2>/dev/null; do sleep 0.1; done + } + + startJournalctl() { +@@ -80,6 +82,7 @@ writeTestUnitNUMAPolicy() { + pid1ReloadWithStrace() { + startStrace + systemctl daemon-reload ++ sleep $sleepAfterStart + stopStrace + } + diff --git a/SOURCES/0337-test-skip-the-test-on-systems-without-NUMA-support.patch b/SOURCES/0337-test-skip-the-test-on-systems-without-NUMA-support.patch new file mode 100644 index 0000000..eef7e87 --- /dev/null +++ b/SOURCES/0337-test-skip-the-test-on-systems-without-NUMA-support.patch @@ -0,0 +1,36 @@ +From ad3e4a0f010c9497b01d89de54213af982f8afd2 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 2 Jul 2019 09:52:45 +0200 +Subject: [PATCH] test: skip the test on systems without NUMA support + +(cherry picked from commit b030847163e9bd63d3dd6eec6ac7f336411faba6) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/testsuite.sh | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index daed8fcc1c..4b715d305a 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -123,7 +123,18 @@ systemctlCheckNUMAProperties() { + fi + } + +-checkNUMA ++if ! checkNUMA; then ++ echo >&2 "NUMA is not supported on this machine, skipping the test" ++ ++ # FIXME: add some sanity checks to verify systemd behaves correctly with ++ # NUMA disabled together with NUMAPolicy= and NUMAMask= ++ ++ systemd-analyze log-level info ++ echo OK > /testok ++ ++ exit 0 ++fi ++ + writeTestUnit + + # Create systemd config drop-in directory diff --git a/SOURCES/0338-test-give-strace-some-time-to-initialize.patch b/SOURCES/0338-test-give-strace-some-time-to-initialize.patch new file mode 100644 index 0000000..6cd3426 --- /dev/null +++ b/SOURCES/0338-test-give-strace-some-time-to-initialize.patch @@ -0,0 +1,30 @@ +From 66f6f6304d87b2fe0c4f91373c7d1b836de1b054 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 23 Jul 2019 00:56:04 +0200 +Subject: [PATCH] test: give strace some time to initialize + +The `coproc` implementation seems to be a little bit different in older +bash versions, so the `strace` is sometimes started AFTER `systemctl +daemon-reload`, which causes unexpected fails. Let's help it a little by +sleeping for a bit. + +(cherry picked from commit c7367d7cfdfdcec98f8659f0ed3f1d7b77123903) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/testsuite.sh | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index 4b715d305a..1c8cf7e6b6 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -34,6 +34,8 @@ journalCursorFile="jounalCursorFile" + + startStrace() { + coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1 ++ # Wait for strace to properly "initialize" ++ sleep $sleepAfterStart + } + + stopStrace() { diff --git a/SOURCES/0339-test-add-a-simple-sanity-check-for-systems-without-N.patch b/SOURCES/0339-test-add-a-simple-sanity-check-for-systems-without-N.patch new file mode 100644 index 0000000..2bb77a3 --- /dev/null +++ b/SOURCES/0339-test-add-a-simple-sanity-check-for-systems-without-N.patch @@ -0,0 +1,391 @@ +From 8239ecf0b4b8bbe5b3c17964d230d13cee4d900a Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Mon, 5 Aug 2019 14:38:45 +0200 +Subject: [PATCH] test: add a simple sanity check for systems without NUMA + support + +(cherry picked from commit 92f8e978923f962a57d744c5f358520ac06f7892) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/testsuite.sh | 350 ++++++++++++++------------- + 1 file changed, 180 insertions(+), 170 deletions(-) + +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index 1c8cf7e6b6..a5ac788178 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -50,11 +50,12 @@ startJournalctl() { + } + + stopJournalctl() { ++ local unit="${1:-init.scope}" + # Using journalctl --sync should be better than using SIGRTMIN+1, as + # the --sync wait until the synchronization is complete + echo "Force journald to write all queued messages" + journalctl --sync +- journalctl -u init.scope --cursor-file="$journalCursorFile" > "$journalLog" ++ journalctl -u $unit --cursor-file="$journalCursorFile" > "$journalLog" + } + + checkNUMA() { +@@ -125,181 +126,190 @@ systemctlCheckNUMAProperties() { + fi + } + +-if ! checkNUMA; then +- echo >&2 "NUMA is not supported on this machine, skipping the test" +- +- # FIXME: add some sanity checks to verify systemd behaves correctly with +- # NUMA disabled together with NUMAPolicy= and NUMAMask= +- +- systemd-analyze log-level info +- echo OK > /testok +- +- exit 0 +-fi +- + writeTestUnit + + # Create systemd config drop-in directory + confDir="/etc/systemd/system.conf.d/" + mkdir -p "$confDir" + +-echo "PID1 NUMAPolicy support - Default policy w/o mask" +-writePID1NUMAPolicy "default" +-pid1ReloadWithStrace +-# Kernel requires that nodemask argument is set to NULL when setting default policy +-grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog +- +-echo "PID1 NUMAPolicy support - Default policy w/ mask" +-writePID1NUMAPolicy "default" "0" +-pid1ReloadWithStrace +-grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog +- +-echo "PID1 NUMAPolicy support - Bind policy w/o mask" +-writePID1NUMAPolicy "bind" +-pid1ReloadWithJournal +-grep "Failed to set NUMA memory policy: Invalid argument" $journalLog +- +-echo "PID1 NUMAPolicy support - Bind policy w/ mask" +-writePID1NUMAPolicy "bind" "0" +-pid1ReloadWithStrace +-grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog +- +-echo "PID1 NUMAPolicy support - Interleave policy w/o mask" +-writePID1NUMAPolicy "interleave" +-pid1ReloadWithJournal +-grep "Failed to set NUMA memory policy: Invalid argument" $journalLog +- +-echo "PID1 NUMAPolicy support - Interleave policy w/ mask" +-writePID1NUMAPolicy "interleave" "0" +-pid1ReloadWithStrace +-grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog +- +-echo "PID1 NUMAPolicy support - Preferred policy w/o mask" +-writePID1NUMAPolicy "preferred" +-pid1ReloadWithJournal +-# Preferred policy with empty node mask is actually allowed and should reset allocation policy to default +-! grep "Failed to set NUMA memory policy: Invalid argument" $journalLog +- +-echo "PID1 NUMAPolicy support - Preferred policy w/ mask" +-writePID1NUMAPolicy "preferred" "0" +-pid1ReloadWithStrace +-grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog +- +-echo "PID1 NUMAPolicy support - Local policy w/o mask" +-writePID1NUMAPolicy "local" +-pid1ReloadWithStrace +-# Kernel requires that nodemask argument is set to NULL when setting default policy +-# The unpatched versions of strace don't recognize the MPOL_LOCAL constant and +-# return a numerical constant instead (with a comment): +-# set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0 +-# Let's cover this scenario as well +-grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog +- +-echo "PID1 NUMAPolicy support - Local policy w/ mask" +-writePID1NUMAPolicy "local" "0" +-pid1ReloadWithStrace +-grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog +- +-echo "Unit file NUMAPolicy support - Default policy w/o mask" +-writeTestUnitNUMAPolicy "default" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "default" +-pid1StopUnit $testUnit +-grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog +- +-echo "Unit file NUMAPolicy support - Default policy w/ mask" +-writeTestUnitNUMAPolicy "default" "0" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "default" "0" +-pid1StopUnit $testUnit +-# Maks must be ignored +-grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog +- +-echo "Unit file NUMAPolicy support - Bind policy w/o mask" +-writeTestUnitNUMAPolicy "bind" +-pid1StartUnitWithJournal $testUnit +-pid1StopUnit $testUnit +-grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog +- +-echo "Unit file NUMAPolicy support - Bind policy w/ mask" +-writeTestUnitNUMAPolicy "bind" "0" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "bind" "0" +-pid1StopUnit $testUnit +-grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog +- +-echo "Unit file NUMAPolicy support - Interleave policy w/o mask" +-writeTestUnitNUMAPolicy "interleave" +-pid1StartUnitWithStrace $testUnit +-pid1StopUnit $testUnit +-grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog +- +-echo "Unit file NUMAPolicy support - Interleave policy w/ mask" +-writeTestUnitNUMAPolicy "interleave" "0" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "interleave" "0" +-pid1StopUnit $testUnit +-grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog +- +-echo "Unit file NUMAPolicy support - Preferred policy w/o mask" +-writeTestUnitNUMAPolicy "preferred" +-pid1StartUnitWithJournal $testUnit +-systemctlCheckNUMAProperties $testUnit "preferred" +-pid1StopUnit $testUnit +-! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog +- +-echo "Unit file NUMAPolicy support - Preferred policy w/ mask" +-writeTestUnitNUMAPolicy "preferred" "0" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "preferred" "0" +-pid1StopUnit $testUnit +-grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog +- +-echo "Unit file NUMAPolicy support - Local policy w/o mask" +-writeTestUnitNUMAPolicy "local" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "local" +-pid1StopUnit $testUnit +-grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog +- +-echo "Unit file NUMAPolicy support - Local policy w/ mask" +-writeTestUnitNUMAPolicy "local" "0" +-pid1StartUnitWithStrace $testUnit +-systemctlCheckNUMAProperties $testUnit "local" "0" +-pid1StopUnit $testUnit +-# Maks must be ignored +-grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog +- +-echo "systemd-run NUMAPolicy support" +-runUnit='numa-systemd-run-test.service' +- +-systemd-run -p NUMAPolicy=default --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "default" +-pid1StopUnit $runUnit +- +-systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "default" "" +-pid1StopUnit $runUnit +- +-systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "bind" "0" +-pid1StopUnit $runUnit +- +-systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "interleave" "0" +-pid1StopUnit $runUnit +- +-systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "preferred" "0" +-pid1StopUnit $runUnit +- +-systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "local" +-pid1StopUnit $runUnit +- +-systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000 +-systemctlCheckNUMAProperties $runUnit "local" "" +-pid1StopUnit $runUnit ++if ! checkNUMA; then ++ echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check" ++ ++ echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support" ++ writePID1NUMAPolicy "default" "0" ++ startJournalctl ++ systemctl daemon-reload ++ stopJournalctl ++ grep "NUMA support not available, ignoring" "$journalLog" ++ ++ echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support" ++ runUnit='numa-systemd-run-test.service' ++ startJournalctl ++ systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000 ++ sleep $sleepAfterStart ++ pid1StopUnit $runUnit ++ stopJournalctl $runUnit ++ grep "NUMA support not available, ignoring" "$journalLog" ++ ++else ++ echo "PID1 NUMAPolicy support - Default policy w/o mask" ++ writePID1NUMAPolicy "default" ++ pid1ReloadWithStrace ++ # Kernel requires that nodemask argument is set to NULL when setting default policy ++ grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++ echo "PID1 NUMAPolicy support - Default policy w/ mask" ++ writePID1NUMAPolicy "default" "0" ++ pid1ReloadWithStrace ++ grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++ echo "PID1 NUMAPolicy support - Bind policy w/o mask" ++ writePID1NUMAPolicy "bind" ++ pid1ReloadWithJournal ++ grep "Failed to set NUMA memory policy: Invalid argument" $journalLog ++ ++ echo "PID1 NUMAPolicy support - Bind policy w/ mask" ++ writePID1NUMAPolicy "bind" "0" ++ pid1ReloadWithStrace ++ grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog ++ ++ echo "PID1 NUMAPolicy support - Interleave policy w/o mask" ++ writePID1NUMAPolicy "interleave" ++ pid1ReloadWithJournal ++ grep "Failed to set NUMA memory policy: Invalid argument" $journalLog ++ ++ echo "PID1 NUMAPolicy support - Interleave policy w/ mask" ++ writePID1NUMAPolicy "interleave" "0" ++ pid1ReloadWithStrace ++ grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog ++ ++ echo "PID1 NUMAPolicy support - Preferred policy w/o mask" ++ writePID1NUMAPolicy "preferred" ++ pid1ReloadWithJournal ++ # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default ++ ! grep "Failed to set NUMA memory policy: Invalid argument" $journalLog ++ ++ echo "PID1 NUMAPolicy support - Preferred policy w/ mask" ++ writePID1NUMAPolicy "preferred" "0" ++ pid1ReloadWithStrace ++ grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog ++ ++ echo "PID1 NUMAPolicy support - Local policy w/o mask" ++ writePID1NUMAPolicy "local" ++ pid1ReloadWithStrace ++ # Kernel requires that nodemask argument is set to NULL when setting default policy ++ # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and ++ # return a numerical constant instead (with a comment): ++ # set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0 ++ # Let's cover this scenario as well ++ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog ++ ++ echo "PID1 NUMAPolicy support - Local policy w/ mask" ++ writePID1NUMAPolicy "local" "0" ++ pid1ReloadWithStrace ++ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Default policy w/o mask" ++ writeTestUnitNUMAPolicy "default" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "default" ++ pid1StopUnit $testUnit ++ grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Default policy w/ mask" ++ writeTestUnitNUMAPolicy "default" "0" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "default" "0" ++ pid1StopUnit $testUnit ++ # Maks must be ignored ++ grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Bind policy w/o mask" ++ writeTestUnitNUMAPolicy "bind" ++ pid1StartUnitWithJournal $testUnit ++ pid1StopUnit $testUnit ++ grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog ++ ++ echo "Unit file NUMAPolicy support - Bind policy w/ mask" ++ writeTestUnitNUMAPolicy "bind" "0" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "bind" "0" ++ pid1StopUnit $testUnit ++ grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Interleave policy w/o mask" ++ writeTestUnitNUMAPolicy "interleave" ++ pid1StartUnitWithStrace $testUnit ++ pid1StopUnit $testUnit ++ grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog ++ ++ echo "Unit file NUMAPolicy support - Interleave policy w/ mask" ++ writeTestUnitNUMAPolicy "interleave" "0" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "interleave" "0" ++ pid1StopUnit $testUnit ++ grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Preferred policy w/o mask" ++ writeTestUnitNUMAPolicy "preferred" ++ pid1StartUnitWithJournal $testUnit ++ systemctlCheckNUMAProperties $testUnit "preferred" ++ pid1StopUnit $testUnit ++ ! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog ++ ++ echo "Unit file NUMAPolicy support - Preferred policy w/ mask" ++ writeTestUnitNUMAPolicy "preferred" "0" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "preferred" "0" ++ pid1StopUnit $testUnit ++ grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Local policy w/o mask" ++ writeTestUnitNUMAPolicy "local" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "local" ++ pid1StopUnit $testUnit ++ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog ++ ++ echo "Unit file NUMAPolicy support - Local policy w/ mask" ++ writeTestUnitNUMAPolicy "local" "0" ++ pid1StartUnitWithStrace $testUnit ++ systemctlCheckNUMAProperties $testUnit "local" "0" ++ pid1StopUnit $testUnit ++ # Maks must be ignored ++ grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog ++ ++ echo "systemd-run NUMAPolicy support" ++ runUnit='numa-systemd-run-test.service' ++ ++ systemd-run -p NUMAPolicy=default --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "default" ++ pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "default" "" ++ pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "bind" "0" ++ pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "interleave" "0" ++ pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "preferred" "0" ++ pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "local" ++ pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "local" "" ++ pid1StopUnit $runUnit ++fi + + # Cleanup + rm -rf $testDir diff --git a/SOURCES/0340-test-drop-the-missed-exit-1-expression.patch b/SOURCES/0340-test-drop-the-missed-exit-1-expression.patch new file mode 100644 index 0000000..716dcf2 --- /dev/null +++ b/SOURCES/0340-test-drop-the-missed-exit-1-expression.patch @@ -0,0 +1,35 @@ +From 772f08f8255d7ab921c344ab4243249cbd1c37fc Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Sat, 10 Aug 2019 16:05:07 +0200 +Subject: [PATCH] test: drop the missed || exit 1 expression + +...as we've already done in the rest of the testsuite, see +cc469c3dfc398210f38f819d367e68646c71d8da + +(cherry picked from commit 67c434b03f8a24f5350f017dfb4b2464406046db) + +Related: #1808940 +--- + test/TEST-36-NUMAPOLICY/test.sh | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh +index f0a321e7a1..3b3b120423 100755 +--- a/test/TEST-36-NUMAPOLICY/test.sh ++++ b/test/TEST-36-NUMAPOLICY/test.sh +@@ -1,5 +1,6 @@ + #!/bin/bash + set -e ++ + TEST_DESCRIPTION="test MUMAPolicy= and NUMAMask= options" + TEST_NO_NSPAWN=1 + QEMU_OPTIONS="-numa node,nodeid=0" +@@ -41,7 +42,7 @@ EOF + cp testsuite.sh $initdir/ + + setup_testsuite +- ) || return 1 ++ ) + setup_nspawn_root + + ddebug "umount $TESTDIR/root" diff --git a/SOURCES/0341-test-replace-cursor-file-with-a-plain-cursor.patch b/SOURCES/0341-test-replace-cursor-file-with-a-plain-cursor.patch new file mode 100644 index 0000000..83a7f55 --- /dev/null +++ b/SOURCES/0341-test-replace-cursor-file-with-a-plain-cursor.patch @@ -0,0 +1,59 @@ +From 0bef8805c81eecfe3960bf00b6022837e4979198 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 3 Mar 2020 15:54:29 +0100 +Subject: [PATCH] test: replace cursor file with a plain cursor + +systemd in RHEL 8 doesn't support the --cursor-file option, so let's +fall back to a plain cursor string + +Related: #1808940 +rhel-only +--- + test/TEST-36-NUMAPOLICY/test.sh | 2 +- + test/TEST-36-NUMAPOLICY/testsuite.sh | 6 +++--- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh +index 3b3b120423..7cc909765b 100755 +--- a/test/TEST-36-NUMAPOLICY/test.sh ++++ b/test/TEST-36-NUMAPOLICY/test.sh +@@ -17,7 +17,7 @@ test_setup() { + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment +- dracut_install mktemp ++ dracut_install mktemp awk + + # mask some services that we do not want to run in these tests + ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index a5ac788178..bffac4ffe6 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -30,7 +30,7 @@ journalSleep=5 + sleepAfterStart=1 + + # Journal cursor for easier navigation +-journalCursorFile="jounalCursorFile" ++journalCursor="" + + startStrace() { + coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1 +@@ -46,7 +46,7 @@ stopStrace() { + + startJournalctl() { + # Save journal's cursor for later navigation +- journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat ++ journalCursor="$(journalctl --no-pager --show-cursor -n0 -ocat | awk '{print $3}')" + } + + stopJournalctl() { +@@ -55,7 +55,7 @@ stopJournalctl() { + # the --sync wait until the synchronization is complete + echo "Force journald to write all queued messages" + journalctl --sync +- journalctl -u $unit --cursor-file="$journalCursorFile" > "$journalLog" ++ journalctl -u $unit --after-cursor="$journalCursor" > "$journalLog" + } + + checkNUMA() { diff --git a/SOURCES/0342-cryptsetup-Treat-key-file-errors-as-a-failed-passwor.patch b/SOURCES/0342-cryptsetup-Treat-key-file-errors-as-a-failed-passwor.patch new file mode 100644 index 0000000..4fee429 --- /dev/null +++ b/SOURCES/0342-cryptsetup-Treat-key-file-errors-as-a-failed-passwor.patch @@ -0,0 +1,32 @@ +From ed282d8d84fa32aaef21994d92d1d3dbfa281094 Mon Sep 17 00:00:00 2001 +From: Ryan Gonzalez +Date: Fri, 22 Feb 2019 23:45:03 -0600 +Subject: [PATCH] cryptsetup: Treat key file errors as a failed password + attempt + +6f177c7dc092eb68762b4533d41b14244adb2a73 caused key file errors to immediately fail, which would make it hard to correct an issue due to e.g. a crypttab typo or a damaged key file. + +Closes #11723. + +(cherry picked from commit c20db3887569e0c0d9c0e2845c5286e7edf0133a) + +Related: #1763155 +--- + src/cryptsetup/cryptsetup.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 33c215eaa1..11162eb722 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -558,6 +558,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 { diff --git a/SOURCES/0343-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch b/SOURCES/0343-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch new file mode 100644 index 0000000..b196d0e --- /dev/null +++ b/SOURCES/0343-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch @@ -0,0 +1,79 @@ +From b89a1a9d19aa806feb984c8dba25116b5b5a52bc Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Wed, 24 Jul 2019 23:54:48 -0400 +Subject: [PATCH] swap: finish the secondary swap units' jobs if deactivation + of the primary swap unit fails + +Currently, if deactivation of the primary swap unit fails: + + # LANG=C systemctl --no-pager stop dev-mapper-fedora\\x2dswap.swap + Job for dev-mapper-fedora\x2dswap.swap failed. + See "systemctl status "dev-mapper-fedora\\x2dswap.swap"" and "journalctl -xe" for details. + +then there are still the running stop jobs for all the secondary swap units +that follow the primary one: + + # systemctl list-jobs + JOB UNIT TYPE STATE + 3233 dev-disk-by\x2duuid-2dc8b9b1\x2da0a5\x2d44d8\x2d89c4\x2d6cdd26cd5ce0.swap stop running + 3232 dev-dm\x2d1.swap stop running + 3231 dev-disk-by\x2did-dm\x2duuid\x2dLVM\x2dyuXWpCCIurGzz2nkGCVnUFSi7GH6E3ZcQjkKLnF0Fil0RJmhoLN8fcOnDybWCMTj.swap stop running + 3230 dev-disk-by\x2did-dm\x2dname\x2dfedora\x2dswap.swap stop running + 3234 dev-fedora-swap.swap stop running + + 5 jobs listed. + +This remains endlessly because their JobTimeoutUSec is infinity: + + # LANG=C systemctl show -p JobTimeoutUSec dev-fedora-swap.swap + JobTimeoutUSec=infinity + +If this issue happens during system shutdown, the system shutdown appears to +get hang and the system will be forcibly shutdown or rebooted 30 minutes later +by the following configuration: + + # grep -E "^JobTimeout" /usr/lib/systemd/system/reboot.target + JobTimeoutSec=30min + JobTimeoutAction=reboot-force + +The scenario in the real world seems that there is some service unit with +KillMode=none, processes whose memory is being swapped out are not killed +during stop operation in the service unit and then swapoff command fails. + +On the other hand, it works well in successful case of swapoff command because +the secondary jobs monitor /proc/swaps file and can detect deletion of the +corresponding swap file. + +This commit fixes the issue by finishing the secondary swap units' jobs if +deactivation of the primary swap unit fails. + +Fixes: #11577 +(cherry picked from commit 9c1f969d40f84d5cc98d810bab8b24148b2d8928) + +Resolves: #1749622 +--- + src/core/swap.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/core/swap.c b/src/core/swap.c +index e717dbb54a..66a62d8a37 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -682,9 +682,15 @@ static void swap_enter_active(Swap *s, SwapResult f) { + static void swap_enter_dead_or_active(Swap *s, SwapResult f) { + assert(s); + +- if (s->from_proc_swaps) ++ if (s->from_proc_swaps) { ++ Swap *other; ++ + swap_enter_active(s, f); +- else ++ ++ LIST_FOREACH_OTHERS(same_devnode, other, s) ++ if (UNIT(other)->job) ++ swap_enter_dead_or_active(other, f); ++ } else + swap_enter_dead(s, f); + } + diff --git a/SOURCES/0344-resolved-Recover-missing-PrivateTmp-yes-and-ProtectS.patch b/SOURCES/0344-resolved-Recover-missing-PrivateTmp-yes-and-ProtectS.patch new file mode 100644 index 0000000..9c59bdc --- /dev/null +++ b/SOURCES/0344-resolved-Recover-missing-PrivateTmp-yes-and-ProtectS.patch @@ -0,0 +1,41 @@ +From d9ae3222cfbd5d2a48e6dbade6617085cc76f1c1 Mon Sep 17 00:00:00 2001 +From: HATAYAMA Daisuke +Date: Tue, 25 Feb 2020 13:35:50 -0500 +Subject: [PATCH] resolved: Recover missing PrivateTmp=yes and + ProtectSystem=strict + +Since the commit b61e8046ebcb28225423fc0073183d68d4c577c4, +systemd-resolved.service often fails to start with the following message: + + Failed at step NAMESPACE spawning /usr/bin/mount: Read-only file system + +This is because dropping DynamicUser=yes dropped implicit PrivateTmp=yes and +also implicit After=systemd-tmpfiles-setup.service, and thus +systemd-resolved.service can start before systemd-remount-fs.service. As a +result, mount operations associated with PrivateDevices= can be performed to +still read-only filesystems. + +To fix this issue, it's better to recover PrivateTmp=yes and +ProtectSystem=strict just as the upstream commit +62fb7e80fcc45a1530ed58a84980be8cfafa9b3e (Revert "resolve: enable DynamicUser= +for systemd-resolved.service"). + +Resolves: #1810869 +--- + units/systemd-resolved.service.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in +index 6c2ad5ca86..aad1a53a5f 100644 +--- a/units/systemd-resolved.service.in ++++ b/units/systemd-resolved.service.in +@@ -28,7 +28,9 @@ WatchdogSec=3min + User=systemd-resolve + CapabilityBoundingSet=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + AmbientCapabilities=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE ++PrivateTmp=yes + PrivateDevices=yes ++ProtectSystems=strict + ProtectHome=yes + ProtectControlGroups=yes + ProtectKernelTunables=yes diff --git a/SOURCES/0345-bus_open-leak-sd_event_source-when-udevadm-trigger.patch b/SOURCES/0345-bus_open-leak-sd_event_source-when-udevadm-trigger.patch new file mode 100644 index 0000000..b5536da --- /dev/null +++ b/SOURCES/0345-bus_open-leak-sd_event_source-when-udevadm-trigger.patch @@ -0,0 +1,30 @@ +From 448b34284c09469eaa2168291ccb34afc3e4cc1d Mon Sep 17 00:00:00 2001 +From: ven <2988994+hexiaowen@users.noreply.github.com> +Date: Wed, 22 May 2019 14:24:28 +0800 +Subject: [PATCH] =?UTF-8?q?bus=5Fopen=20leak=20sd=5Fevent=5Fsource=20when?= + =?UTF-8?q?=20udevadm=20trigger=E3=80=82?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On my host, when executing the udevadm trigger, I only receive the change event, which causes memleak + +(cherry picked from commit b2774a3ae692113e1f47a336a6c09bac9cfb49ad) + +Resolves: #1798504 +--- + src/login/logind-button.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/login/logind-button.c b/src/login/logind-button.c +index 0defa6b9ba..9944eb2316 100644 +--- a/src/login/logind-button.c ++++ b/src/login/logind-button.c +@@ -341,6 +341,7 @@ int button_open(Button *b) { + + (void) button_set_mask(b); + ++ b->io_event_source = sd_event_source_unref(b->io_event_source); + r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); + if (r < 0) { + log_error_errno(r, "Failed to add button event: %m"); diff --git a/SOURCES/0346-core-rework-StopWhenUnneeded-logic.patch b/SOURCES/0346-core-rework-StopWhenUnneeded-logic.patch new file mode 100644 index 0000000..fcddae3 --- /dev/null +++ b/SOURCES/0346-core-rework-StopWhenUnneeded-logic.patch @@ -0,0 +1,339 @@ +From 5458256264c97eee521caf07c705f549a0f0bd55 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 9 Aug 2018 16:26:27 +0200 +Subject: [PATCH] core: rework StopWhenUnneeded= logic + +Previously, we'd act immediately on StopWhenUnneeded= when a unit state +changes. With this rework we'll maintain a queue instead: whenever +there's the chance that StopWhenUneeded= might have an effect we enqueue +the unit, and process it later when we have nothing better to do. + +This should make the implementation a bit more reliable, as the unit notify event +cannot immediately enqueue tons of side-effect jobs that might +contradict each other, but we do so only in a strictly ordered fashion, +from the main event loop. + +This slightly changes the check when to consider a unit "unneeded". +Previously, we'd assume that a unit in "deactivating" state could also +be cleaned up. With this new logic we'll only consider units unneeded +that are fully up and have no job queued. This means that whenever +there's something pending for a unit we won't clean it up. + +(cherry picked from commit a3c1168ac293f16d9343d248795bb4c246aaff4a) + +Resolves: #1798046 +--- + src/core/manager.c | 43 ++++++++++++++++ + src/core/manager.h | 3 ++ + src/core/unit.c | 122 +++++++++++++++++++++++++-------------------- + src/core/unit.h | 7 +++ + 4 files changed, 120 insertions(+), 55 deletions(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index 0eae7d46fb..4c04896aaa 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1211,6 +1211,45 @@ static unsigned manager_dispatch_gc_job_queue(Manager *m) { + return n; + } + ++static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) { ++ unsigned n = 0; ++ Unit *u; ++ int r; ++ ++ assert(m); ++ ++ while ((u = m->stop_when_unneeded_queue)) { ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ assert(m->stop_when_unneeded_queue); ++ ++ assert(u->in_stop_when_unneeded_queue); ++ LIST_REMOVE(stop_when_unneeded_queue, m->stop_when_unneeded_queue, u); ++ u->in_stop_when_unneeded_queue = false; ++ ++ n++; ++ ++ if (!unit_is_unneeded(u)) ++ continue; ++ ++ log_unit_debug(u, "Unit is not needed anymore."); ++ ++ /* If stopping a unit fails continuously we might enter a stop loop here, hence stop acting on the ++ * service being unnecessary after a while. */ ++ ++ if (!ratelimit_below(&u->auto_stop_ratelimit)) { ++ log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently."); ++ continue; ++ } ++ ++ /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ ++ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); ++ if (r < 0) ++ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); ++ } ++ ++ return n; ++} ++ + static void manager_clear_jobs_and_units(Manager *m) { + Unit *u; + +@@ -1228,6 +1267,7 @@ static void manager_clear_jobs_and_units(Manager *m) { + assert(!m->cleanup_queue); + assert(!m->gc_unit_queue); + assert(!m->gc_job_queue); ++ assert(!m->stop_when_unneeded_queue); + + assert(hashmap_isempty(m->jobs)); + assert(hashmap_isempty(m->units)); +@@ -2824,6 +2864,9 @@ int manager_loop(Manager *m) { + if (manager_dispatch_cgroup_realize_queue(m) > 0) + continue; + ++ if (manager_dispatch_stop_when_unneeded_queue(m) > 0) ++ continue; ++ + if (manager_dispatch_dbus_queue(m) > 0) + continue; + +diff --git a/src/core/manager.h b/src/core/manager.h +index fa47952d24..40568d3c8b 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -130,6 +130,9 @@ struct Manager { + /* Target units whose default target dependencies haven't been set yet */ + LIST_HEAD(Unit, target_deps_queue); + ++ /* Units that might be subject to StopWhenUnneeded= clean-up */ ++ LIST_HEAD(Unit, stop_when_unneeded_queue); ++ + sd_event *event; + + /* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in +diff --git a/src/core/unit.c b/src/core/unit.c +index e1f5e6f7bd..40f138d25c 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -438,6 +438,22 @@ void unit_add_to_dbus_queue(Unit *u) { + u->in_dbus_queue = true; + } + ++void unit_add_to_stop_when_unneeded_queue(Unit *u) { ++ assert(u); ++ ++ if (u->in_stop_when_unneeded_queue) ++ return; ++ ++ if (!u->stop_when_unneeded) ++ return; ++ ++ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) ++ return; ++ ++ LIST_PREPEND(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u); ++ u->in_stop_when_unneeded_queue = true; ++} ++ + static void bidi_set_free(Unit *u, Hashmap *h) { + Unit *other; + Iterator i; +@@ -634,6 +650,9 @@ void unit_free(Unit *u) { + if (u->in_target_deps_queue) + LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); + ++ if (u->in_stop_when_unneeded_queue) ++ LIST_REMOVE(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u); ++ + safe_close(u->ip_accounting_ingress_map_fd); + safe_close(u->ip_accounting_egress_map_fd); + +@@ -1950,55 +1969,71 @@ bool unit_can_reload(Unit *u) { + return UNIT_VTABLE(u)->reload; + } + +-static void unit_check_unneeded(Unit *u) { +- +- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- +- static const UnitDependency needed_dependencies[] = { ++bool unit_is_unneeded(Unit *u) { ++ static const UnitDependency deps[] = { + UNIT_REQUIRED_BY, + UNIT_REQUISITE_OF, + UNIT_WANTED_BY, + UNIT_BOUND_BY, + }; +- +- unsigned j; +- int r; ++ size_t j; + + assert(u); + +- /* If this service shall be shut down when unneeded then do +- * so. */ +- + if (!u->stop_when_unneeded) +- return; ++ return false; + +- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) +- return; ++ /* Don't clean up while the unit is transitioning or is even inactive. */ ++ if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) ++ return false; ++ if (u->job) ++ return false; + +- for (j = 0; j < ELEMENTSOF(needed_dependencies); j++) { ++ for (j = 0; j < ELEMENTSOF(deps); j++) { + Unit *other; + Iterator i; + void *v; + +- HASHMAP_FOREACH_KEY(v, other, u->dependencies[needed_dependencies[j]], i) +- if (unit_active_or_pending(other) || unit_will_restart(other)) +- return; +- } ++ /* If a dependending unit has a job queued, or is active (or in transitioning), or is marked for ++ * restart, then don't clean this one up. */ + +- /* If stopping a unit fails continuously we might enter a stop +- * loop here, hence stop acting on the service being +- * unnecessary after a while. */ +- if (!ratelimit_below(&u->auto_stop_ratelimit)) { +- log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently."); +- return; ++ HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i) { ++ if (u->job) ++ return false; ++ ++ if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) ++ return false; ++ ++ if (unit_will_restart(other)) ++ return false; ++ } + } + +- log_unit_info(u, "Unit not needed anymore. Stopping."); ++ return true; ++} + +- /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ +- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); +- if (r < 0) +- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); ++static void check_unneeded_dependencies(Unit *u) { ++ ++ static const UnitDependency deps[] = { ++ UNIT_REQUIRES, ++ UNIT_REQUISITE, ++ UNIT_WANTS, ++ UNIT_BINDS_TO, ++ }; ++ size_t j; ++ ++ assert(u); ++ ++ /* Add all units this unit depends on to the queue that processes StopWhenUnneeded= behaviour. */ ++ ++ for (j = 0; j < ELEMENTSOF(deps); j++) { ++ Unit *other; ++ Iterator i; ++ void *v; ++ ++ HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i) ++ unit_add_to_stop_when_unneeded_queue(other); ++ } + } + + static void unit_check_binds_to(Unit *u) { +@@ -2098,29 +2133,6 @@ static void retroactively_stop_dependencies(Unit *u) { + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); + } + +-static void check_unneeded_dependencies(Unit *u) { +- Unit *other; +- Iterator i; +- void *v; +- +- assert(u); +- assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); +- +- /* Garbage collect services that might not be needed anymore, if enabled */ +- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUISITE], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +-} +- + void unit_start_on_failure(Unit *u) { + Unit *other; + Iterator i; +@@ -2423,7 +2435,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag + } + + /* stop unneeded units regardless if going down was expected or not */ +- if (UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) + check_unneeded_dependencies(u); + + if (ns != os && ns == UNIT_FAILED) { +@@ -2483,7 +2495,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag + + if (!MANAGER_IS_RELOADING(u->manager)) { + /* Maybe we finished startup and are now ready for being stopped because unneeded? */ +- unit_check_unneeded(u); ++ unit_add_to_stop_when_unneeded_queue(u); + + /* Maybe we finished startup, but something we needed has vanished? Let's die then. (This happens when + * something BindsTo= to a Type=oneshot unit, as these units go directly from starting to inactive, +diff --git a/src/core/unit.h b/src/core/unit.h +index 99755823eb..595ee88d43 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -212,6 +212,9 @@ typedef struct Unit { + /* Target dependencies queue */ + LIST_FIELDS(Unit, target_deps_queue); + ++ /* Queue of units with StopWhenUnneeded set that shell be checked for clean-up. */ ++ LIST_FIELDS(Unit, stop_when_unneeded_queue); ++ + /* PIDs we keep an eye on. Note that a unit might have many + * more, but these are the ones we care enough about to + * process SIGCHLD for */ +@@ -322,6 +325,7 @@ typedef struct Unit { + bool in_cgroup_realize_queue:1; + bool in_cgroup_empty_queue:1; + bool in_target_deps_queue:1; ++ bool in_stop_when_unneeded_queue:1; + + bool sent_dbus_new_signal:1; + +@@ -615,6 +619,7 @@ void unit_add_to_dbus_queue(Unit *u); + void unit_add_to_cleanup_queue(Unit *u); + void unit_add_to_gc_queue(Unit *u); + void unit_add_to_target_deps_queue(Unit *u); ++void unit_add_to_stop_when_unneeded_queue(Unit *u); + + int unit_merge(Unit *u, Unit *other); + int unit_merge_by_name(Unit *u, const char *other); +@@ -751,6 +756,8 @@ bool unit_type_supported(UnitType t); + + bool unit_is_pristine(Unit *u); + ++bool unit_is_unneeded(Unit *u); ++ + pid_t unit_control_pid(Unit *u); + pid_t unit_main_pid(Unit *u); + diff --git a/SOURCES/0347-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch b/SOURCES/0347-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch new file mode 100644 index 0000000..0431571 --- /dev/null +++ b/SOURCES/0347-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch @@ -0,0 +1,81 @@ +From 6ecf0a945d6d4187995de7e79e3ed75f4827289a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 24 Nov 2019 14:14:43 +0100 +Subject: [PATCH] pid1: fix the names of AllowedCPUs= and AllowedMemoryNodes= + +The original PR was submitted with CPUSetCpus and CPUSetMems, which was later +changed to AllowedCPUs and AllowedMemmoryNodes everywhere (including the parser +used by systemd-run), but not in the parser for unit files. + +Since we already released -rc1, let's keep support for the old names. I think +we can remove it in a release or two if anyone remembers to do that. + +Fixes #14126. Follow-up for 047f5d63d7a1ab75073f8485e2f9b550d25b0772. + +(cherry picked from commit 0b8d3075872a05e0449906d24421ce192f50c29f) + +Related: #1818054 +--- + src/core/load-fragment-gperf.gperf.m4 | 4 ++-- + src/core/load-fragment.c | 4 ++-- + src/core/load-fragment.h | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index ebb44df487..161c5a2c82 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -161,14 +161,14 @@ $1.KillSignal, config_parse_signal, 0, + )m4_dnl + m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS', + `$1.Slice, config_parse_unit_slice, 0, 0 ++$1.AllowedCPUs, config_parse_allowed_cpus, 0, offsetof($1, cgroup_context) ++$1.AllowedMemoryNodes, config_parse_allowed_mems, 0, offsetof($1, cgroup_context) + $1.CPUAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.cpu_accounting) + $1.CPUWeight, config_parse_cg_weight, 0, offsetof($1, cgroup_context.cpu_weight) + $1.StartupCPUWeight, config_parse_cg_weight, 0, offsetof($1, cgroup_context.startup_cpu_weight) + $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.CPUSetCpus, config_parse_cpuset_cpus, 0, offsetof($1, cgroup_context) +-$1.CPUSetMems, config_parse_cpuset_mems, 0, offsetof($1, cgroup_context) + $1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting) + $1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 6debf82401..2082166afb 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3011,7 +3011,7 @@ int config_parse_cpu_quota( + return 0; + } + +-int config_parse_cpuset_cpus( ++int config_parse_allowed_cpus( + const char *unit, + const char *filename, + unsigned line, +@@ -3030,7 +3030,7 @@ int config_parse_cpuset_cpus( + return 0; + } + +-int config_parse_cpuset_mems( ++int config_parse_allowed_mems( + const char *unit, + const char *filename, + unsigned line, +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index 6612e1fb32..424fa478a7 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -86,8 +86,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_set_status); + CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv); + CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems); + CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota); +-CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_cpus); +-CONFIG_PARSER_PROTOTYPE(config_parse_cpuset_mems); ++CONFIG_PARSER_PROTOTYPE(config_parse_allowed_cpus); ++CONFIG_PARSER_PROTOTYPE(config_parse_allowed_mems); + CONFIG_PARSER_PROTOTYPE(config_parse_protect_home); + CONFIG_PARSER_PROTOTYPE(config_parse_protect_system); + CONFIG_PARSER_PROTOTYPE(config_parse_bus_name); diff --git a/SOURCES/0348-core-fix-re-realization-of-cgroup-siblings.patch b/SOURCES/0348-core-fix-re-realization-of-cgroup-siblings.patch new file mode 100644 index 0000000..173b2e3 --- /dev/null +++ b/SOURCES/0348-core-fix-re-realization-of-cgroup-siblings.patch @@ -0,0 +1,62 @@ +From ca843e40587fb87fe20bd0561b6ccb42aaafc4ab Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 9 Jan 2020 17:30:31 +0100 +Subject: [PATCH] core: fix re-realization of cgroup siblings + +This is a fix-up for eef85c4a3f8054d29383a176f6cebd1ef3a15b9a which +broke this. + +Tracked down by @w-simon + +Fixes: #14453 +(cherry picked from commit 65f6b6bdcb500c576674b5838e4cc4c35e18bfde) + +Related: #1818054 +--- + src/core/cgroup.c | 21 +++++++-------------- + 1 file changed, 7 insertions(+), 14 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 664d269483..3f7665b755 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1796,32 +1796,25 @@ unsigned manager_dispatch_cgroup_realize_queue(Manager *m) { + static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) { + Unit *slice; + +- /* This adds the siblings of the specified unit and the +- * siblings of all parent units to the cgroup queue. (But +- * neither the specified unit itself nor the parents.) */ ++ /* This adds the siblings of the specified unit and the siblings of all parent units to the cgroup ++ * queue. (But neither the specified unit itself nor the parents.) */ + + while ((slice = UNIT_DEREF(u->slice))) { + Iterator i; + Unit *m; + void *v; + +- HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE], i) { +- if (m == u) +- continue; +- +- /* Skip units that have a dependency on the slice +- * but aren't actually in it. */ ++ HASHMAP_FOREACH_KEY(v, m, slice->dependencies[UNIT_BEFORE], i) { ++ /* Skip units that have a dependency on the slice but aren't actually in it. */ + if (UNIT_DEREF(m->slice) != slice) + continue; + +- /* No point in doing cgroup application for units +- * without active processes. */ ++ /* No point in doing cgroup application for units without active processes. */ + if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m))) + continue; + +- /* If the unit doesn't need any new controllers +- * and has current ones realized, it doesn't need +- * any changes. */ ++ /* If the unit doesn't need any new controllers and has current ones realized, it ++ * doesn't need any changes. */ + if (unit_has_mask_realized(m, + unit_get_target_mask(m), + unit_get_enable_mask(m), diff --git a/SOURCES/0349-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch b/SOURCES/0349-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch new file mode 100644 index 0000000..8d42493 --- /dev/null +++ b/SOURCES/0349-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch @@ -0,0 +1,99 @@ +From 9fe3b9c7165afeedcf9f31959c436bcec233bb4d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Tue, 14 Apr 2020 16:16:45 +0200 +Subject: [PATCH] basic: use comma as separator in cpuset cgroup cpu ranges + +This is a workaround for +https://bugzilla.redhat.com/show_bug.cgi?id=1819152 and should be +reverted in RHEL-8.3. + +RHEL-only + +Related: #1818054 +--- + src/basic/cpu-set-util.c | 45 ++++++++++++++++++++++++++++++++++++++++ + src/basic/cpu-set-util.h | 1 + + src/core/cgroup.c | 2 +- + 3 files changed, 47 insertions(+), 1 deletion(-) + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 36cb017ae7..51752ad1a6 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -86,6 +86,51 @@ char *cpu_set_to_range_string(const CPUSet *set) { + return TAKE_PTR(str) ?: strdup(""); + } + ++/* XXX(msekleta): this is the workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1819152, remove in 8.3 */ ++char *cpu_set_to_range_string_kernel(const CPUSet *set) { ++ unsigned range_start = 0, range_end; ++ _cleanup_free_ char *str = NULL; ++ size_t allocated = 0, len = 0; ++ bool in_range = false; ++ int r; ++ ++ for (unsigned i = 0; i < set->allocated * 8; i++) ++ if (CPU_ISSET_S(i, set->allocated, set->set)) { ++ if (in_range) ++ range_end++; ++ else { ++ range_start = range_end = i; ++ in_range = true; ++ } ++ } else if (in_range) { ++ in_range = false; ++ ++ if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(unsigned))) ++ return NULL; ++ ++ if (range_end > range_start) ++ r = sprintf(str + len, len > 0 ? ",%d-%d" : "%d-%d", range_start, range_end); ++ else ++ r = sprintf(str + len, len > 0 ? ",%d" : "%d", range_start); ++ assert_se(r > 0); ++ len += r; ++ } ++ ++ if (in_range) { ++ if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(int))) ++ return NULL; ++ ++ if (range_end > range_start) ++ r = sprintf(str + len, len > 0 ? ",%d-%d" : "%d-%d", range_start, range_end); ++ else ++ r = sprintf(str + len, len > 0 ? ",%d" : "%d", range_start); ++ assert_se(r > 0); ++ } ++ ++ return TAKE_PTR(str) ?: strdup(""); ++} ++ ++ + int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) { + size_t need; + +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 295028cb54..8519a9b6c8 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -27,6 +27,7 @@ int cpu_set_add_all(CPUSet *a, const CPUSet *b); + + char* cpu_set_to_string(const CPUSet *a); + char *cpu_set_to_range_string(const CPUSet *a); ++char *cpu_set_to_range_string_kernel(const CPUSet *a); + int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus); + + int parse_cpu_set_full( +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 3f7665b755..9e4c3c7dac 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -557,7 +557,7 @@ static void cgroup_apply_unified_cpuset(Unit *u, CPUSet cpus, const char *name) + _cleanup_free_ char *buf = NULL; + int r; + +- buf = cpu_set_to_range_string(&cpus); ++ buf = cpu_set_to_range_string_kernel(&cpus); + if (!buf) + return; + diff --git a/SOURCES/0350-core-transition-to-FINAL_SIGTERM-state-after-ExecSto.patch b/SOURCES/0350-core-transition-to-FINAL_SIGTERM-state-after-ExecSto.patch new file mode 100644 index 0000000..e9ae6e2 --- /dev/null +++ b/SOURCES/0350-core-transition-to-FINAL_SIGTERM-state-after-ExecSto.patch @@ -0,0 +1,159 @@ +From 6e732b6e44ad8eb3e94c47459c64f0bc6ef2fcb0 Mon Sep 17 00:00:00 2001 +From: Anita Zhang +Date: Sat, 25 Jan 2020 16:46:16 +0100 +Subject: [PATCH] core: transition to FINAL_SIGTERM state after ExecStopPost= + +Fixes #14566 + +(cherry picked from commit c1566ef0d22ed786b9ecf4c476e53b8a91e67578) + +Resolves: #1766479 +--- + src/core/service.c | 10 +++++ + test/TEST-47-ISSUE-14566/Makefile | 1 + + test/TEST-47-ISSUE-14566/repro.sh | 5 +++ + test/TEST-47-ISSUE-14566/test.sh | 55 +++++++++++++++++++++++++++ + test/TEST-47-ISSUE-14566/testsuite.sh | 23 +++++++++++ + 5 files changed, 94 insertions(+) + create mode 120000 test/TEST-47-ISSUE-14566/Makefile + create mode 100755 test/TEST-47-ISSUE-14566/repro.sh + create mode 100755 test/TEST-47-ISSUE-14566/test.sh + create mode 100755 test/TEST-47-ISSUE-14566/testsuite.sh + +diff --git a/src/core/service.c b/src/core/service.c +index b1ec52d220..5035dcacac 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -3280,6 +3280,12 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + break; + + case SERVICE_STOP_POST: ++ ++ if (control_pid_good(s) <= 0) ++ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); ++ ++ break; ++ + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + +@@ -3415,6 +3421,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + break; + + case SERVICE_STOP_POST: ++ if (main_pid_good(s) <= 0) ++ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f); ++ break; ++ + case SERVICE_FINAL_SIGTERM: + case SERVICE_FINAL_SIGKILL: + if (main_pid_good(s) <= 0) +diff --git a/test/TEST-47-ISSUE-14566/Makefile b/test/TEST-47-ISSUE-14566/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-47-ISSUE-14566/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-47-ISSUE-14566/repro.sh b/test/TEST-47-ISSUE-14566/repro.sh +new file mode 100755 +index 0000000000..5217602257 +--- /dev/null ++++ b/test/TEST-47-ISSUE-14566/repro.sh +@@ -0,0 +1,5 @@ ++#!/bin/bash ++ ++sleep infinity & ++echo $! > /leakedtestpid ++wait $! +diff --git a/test/TEST-47-ISSUE-14566/test.sh b/test/TEST-47-ISSUE-14566/test.sh +new file mode 100755 +index 0000000000..0ce772164a +--- /dev/null ++++ b/test/TEST-47-ISSUE-14566/test.sh +@@ -0,0 +1,55 @@ ++#!/bin/bash ++set -e ++TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over proccesses with ExecStopPost=" ++. $TEST_BASE_DIR/test-functions ++ ++test_setup() { ++ create_empty_image ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ ++ ( ++ LOG_LEVEL=5 ++ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) ++ ++ setup_basic_environment ++ ++ # mask some services that we do not want to run in these tests ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service < $initdir/etc/systemd/system/issue_14566_test.service << EOF ++[Unit] ++Description=Issue 14566 Repro ++ ++[Service] ++ExecStart=/repro.sh ++ExecStopPost=/bin/true ++KillMode=mixed ++EOF ++ ++ cp testsuite.sh $initdir/ ++ cp repro.sh $initdir/ ++ ++ setup_testsuite ++ ) ++ setup_nspawn_root ++ ++ ddebug "umount $TESTDIR/root" ++ umount $TESTDIR/root ++} ++ ++do_test "$@" +diff --git a/test/TEST-47-ISSUE-14566/testsuite.sh b/test/TEST-47-ISSUE-14566/testsuite.sh +new file mode 100755 +index 0000000000..d917cf52ff +--- /dev/null ++++ b/test/TEST-47-ISSUE-14566/testsuite.sh +@@ -0,0 +1,23 @@ ++#!/bin/bash ++set -ex ++set -o pipefail ++ ++systemd-analyze log-level debug ++systemd-analyze log-target console ++ ++systemctl start issue_14566_test ++systemctl status issue_14566_test ++ ++leaked_pid=$(cat /leakedtestpid) ++ ++systemctl stop issue_14566_test ++ ++# Leaked PID will still be around if we're buggy. ++# I personally prefer to see 42. ++ps -p "$leaked_pid" && exit 42 ++ ++systemd-analyze log-level info ++ ++echo OK > /testok ++ ++exit 0 diff --git a/SOURCES/0351-sd-journal-close-journal-files-that-were-deleted-by-.patch b/SOURCES/0351-sd-journal-close-journal-files-that-were-deleted-by-.patch new file mode 100644 index 0000000..ce44405 --- /dev/null +++ b/SOURCES/0351-sd-journal-close-journal-files-that-were-deleted-by-.patch @@ -0,0 +1,75 @@ +From 45275461f4a5293f15191ec5cb3bb80219ef2474 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Tue, 4 Feb 2020 14:23:14 +0100 +Subject: [PATCH] sd-journal: close journal files that were deleted by journald + before we've setup inotify watch + +Fixes #14695 + +(cherry picked from commit 28ca867abdb20d0e4ac1901e2ed669cdb41ea3f6) + +Related: #1796128 +--- + src/journal/journal-file.c | 2 +- + src/journal/journal-file.h | 1 + + src/journal/sd-journal.c | 15 +++++++++++++++ + 3 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c +index 8249b11b23..7ab3c47fc9 100644 +--- a/src/journal/journal-file.c ++++ b/src/journal/journal-file.c +@@ -597,7 +597,7 @@ static int journal_file_verify_header(JournalFile *f) { + return 0; + } + +-static int journal_file_fstat(JournalFile *f) { ++int journal_file_fstat(JournalFile *f) { + int r; + + assert(f); +diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h +index 6a44fd39d2..6069b35234 100644 +--- a/src/journal/journal-file.h ++++ b/src/journal/journal-file.h +@@ -144,6 +144,7 @@ int journal_file_open( + int journal_file_set_offline(JournalFile *f, bool wait); + bool journal_file_is_offlining(JournalFile *f); + JournalFile* journal_file_close(JournalFile *j); ++int journal_file_fstat(JournalFile *f); + DEFINE_TRIVIAL_CLEANUP_FUNC(JournalFile*, journal_file_close); + + int journal_file_open_reliably( +diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c +index 323300baec..c06255e273 100644 +--- a/src/journal/sd-journal.c ++++ b/src/journal/sd-journal.c +@@ -2584,6 +2584,8 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { + assert_return(!journal_pid_changed(j), -ECHILD); + + if (j->inotify_fd < 0) { ++ Iterator i; ++ JournalFile *f; + + /* This is the first invocation, hence create the + * inotify watch */ +@@ -2591,6 +2593,19 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { + if (r < 0) + return r; + ++ /* Server might have done some vacuuming while we weren't watching. ++ Get rid of the deleted files now so they don't stay around indefinitely. */ ++ ORDERED_HASHMAP_FOREACH(f, j->files, i) { ++ r = journal_file_fstat(f); ++ if (r < 0) { ++ log_debug_errno(r,"Failed to fstat() journal file '%s' : %m", f->path); ++ continue; ++ } ++ ++ if (f->last_stat.st_nlink <= 0) ++ remove_file_real(j, f); ++ } ++ + /* The journal might have changed since the context + * object was created and we weren't watching before, + * hence don't wait for anything, and return diff --git a/SOURCES/0352-sd-journal-remove-the-dead-code-and-actually-fix-146.patch b/SOURCES/0352-sd-journal-remove-the-dead-code-and-actually-fix-146.patch new file mode 100644 index 0000000..40e4870 --- /dev/null +++ b/SOURCES/0352-sd-journal-remove-the-dead-code-and-actually-fix-146.patch @@ -0,0 +1,42 @@ +From 0f7ee0007b8267cc66b638a44da6ddd984ece412 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 27 Mar 2020 17:01:59 +0100 +Subject: [PATCH] sd-journal: remove the dead code and actually fix #14695 + +journal_file_fstat() returns an error if we call it on already unlinked +journal file and hence we never reach remove_file_real() which is the +entire point. + +I must have made some mistake while testing the fix that got me thinking +the issue is gone while opposite was true. + +Fixes #14695 + +(cherry picked from commit 8581b9f9732d4c158bb5f773230a65ce77f2c292) + +Resolves: #1796128 +--- + src/journal/sd-journal.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c +index c06255e273..4c502978de 100644 +--- a/src/journal/sd-journal.c ++++ b/src/journal/sd-journal.c +@@ -2597,13 +2597,12 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { + Get rid of the deleted files now so they don't stay around indefinitely. */ + ORDERED_HASHMAP_FOREACH(f, j->files, i) { + r = journal_file_fstat(f); +- if (r < 0) { ++ if (r == -EIDRM) ++ remove_file_real(j, f); ++ else if (r < 0) { + log_debug_errno(r,"Failed to fstat() journal file '%s' : %m", f->path); + continue; + } +- +- if (f->last_stat.st_nlink <= 0) +- remove_file_real(j, f); + } + + /* The journal might have changed since the context diff --git a/SOURCES/0353-udev-downgrade-message-when-we-fail-to-set-inotify-w.patch b/SOURCES/0353-udev-downgrade-message-when-we-fail-to-set-inotify-w.patch new file mode 100644 index 0000000..0df7140 --- /dev/null +++ b/SOURCES/0353-udev-downgrade-message-when-we-fail-to-set-inotify-w.patch @@ -0,0 +1,43 @@ +From 38532765172a4e60624b9c24b8d081b34d9f7b86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 13 Nov 2018 14:53:04 +0100 +Subject: [PATCH] udev: downgrade message when we fail to set inotify watch up + +My logs are full of: + +systemd-udevd[6586]: seq 13515 queued, 'add' 'block' +systemd-udevd[6586]: seq 13516 queued, 'change' 'block' +systemd-udevd[6586]: seq 13517 queued, 'change' 'block' +systemd-udevd[6586]: seq 13518 queued, 'remove' 'bdi' +systemd-udevd[6586]: seq 13519 queued, 'remove' 'block' +systemd-udevd[9865]: seq 13514 processed +systemd-udevd[9865]: seq 13515 running +systemd-udevd[9865]: GROUP 6 /usr/lib/udev/rules.d/50-udev-default.rules:59 +systemd-udevd[9865]: IMPORT builtin 'blkid' /usr/lib/udev/rules.d/60-persistent-storage.rules:95 +systemd-udevd[9865]: IMPORT builtin 'blkid' fails: No such file or directory +systemd-udevd[9865]: loop4: Failed to add device '/dev/loop4' to watch: No such file or directory +(the last line is at error level). +If we are too slow to set up a watch and the device is already gone by the time +we try, this is not an error. + +(cherry picked from commit 7fe0d0d5c0ad5aa3f069bb282868938d414d7ad1) + +Resolves: #1808051 +--- + src/udev/udev-watch.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c +index 7864f57aa5..9c82196add 100644 +--- a/src/udev/udev-watch.c ++++ b/src/udev/udev-watch.c +@@ -87,7 +87,8 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) { + log_debug("adding watch on '%s'", udev_device_get_devnode(dev)); + wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); + if (wd < 0) { +- log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m", ++ log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, ++ errno, "inotify_add_watch(%d, %s, %o) failed: %m", + inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); + return; + } diff --git a/SOURCES/0354-logind-check-PolicyKit-before-allowing-VT-switch.patch b/SOURCES/0354-logind-check-PolicyKit-before-allowing-VT-switch.patch new file mode 100644 index 0000000..6eacf48 --- /dev/null +++ b/SOURCES/0354-logind-check-PolicyKit-before-allowing-VT-switch.patch @@ -0,0 +1,195 @@ +From af20a66874296f71618819ebce9d4335b195728c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 22 Jan 2020 12:04:38 +0100 +Subject: [PATCH] logind: check PolicyKit before allowing VT switch + +Let's lock this down a bit. Effectively nothing much changes, since the +default PK policy will allow users on the VT to change VT. Only users +with no local VT session won't be able to switch VTs. + +(cherry picked from commit 4acf0cfd2f92edb94ad48d04f1ce6c9ab4e19d55) + +Resolves: #1797679 +--- + src/login/logind-dbus.c | 16 +++++++ + src/login/logind-seat-dbus.c | 58 ++++++++++++++++++++++++- + src/login/logind-session-dbus.c | 15 +++++++ + src/login/org.freedesktop.login1.policy | 10 +++++ + 4 files changed, 98 insertions(+), 1 deletion(-) + +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index dca7f4a30f..3f122fcbd9 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -913,6 +913,8 @@ static int method_activate_session(sd_bus_message *message, void *userdata, sd_b + if (r < 0) + return r; + ++ /* PolicyKit is done by bus_session_method_activate() */ ++ + return bus_session_method_activate(message, session, error); + } + +@@ -944,6 +946,20 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda + 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); + ++ r = bus_verify_polkit_async( ++ message, ++ CAP_SYS_ADMIN, ++ "org.freedesktop.login1.chvt", ++ NULL, ++ false, ++ UID_INVALID, ++ &m->polkit_registry, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Will call us back */ ++ + r = session_activate(session); + if (r < 0) + return r; +diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c +index c4d9b067c6..2e590a8f21 100644 +--- a/src/login/logind-seat-dbus.c ++++ b/src/login/logind-seat-dbus.c +@@ -174,6 +174,20 @@ static int method_activate_session(sd_bus_message *message, void *userdata, sd_b + if (session->seat != s) + return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id); + ++ r = bus_verify_polkit_async( ++ message, ++ CAP_SYS_ADMIN, ++ "org.freedesktop.login1.chvt", ++ NULL, ++ false, ++ UID_INVALID, ++ &s->manager->polkit_registry, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Will call us back */ ++ + r = session_activate(session); + if (r < 0) + return r; +@@ -194,7 +208,21 @@ static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_erro + return r; + + if (to <= 0) +- return -EINVAL; ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal"); ++ ++ r = bus_verify_polkit_async( ++ message, ++ CAP_SYS_ADMIN, ++ "org.freedesktop.login1.chvt", ++ NULL, ++ false, ++ UID_INVALID, ++ &s->manager->polkit_registry, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Will call us back */ + + r = seat_switch_to(s, to); + if (r < 0) +@@ -210,6 +238,20 @@ static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus + assert(message); + assert(s); + ++ r = bus_verify_polkit_async( ++ message, ++ CAP_SYS_ADMIN, ++ "org.freedesktop.login1.chvt", ++ NULL, ++ false, ++ UID_INVALID, ++ &s->manager->polkit_registry, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Will call us back */ ++ + r = seat_switch_to_next(s); + if (r < 0) + return r; +@@ -224,6 +266,20 @@ static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd + assert(message); + assert(s); + ++ r = bus_verify_polkit_async( ++ message, ++ CAP_SYS_ADMIN, ++ "org.freedesktop.login1.chvt", ++ NULL, ++ false, ++ UID_INVALID, ++ &s->manager->polkit_registry, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Will call us back */ ++ + r = seat_switch_to_previous(s); + if (r < 0) + return r; +diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c +index 25c4981dc0..88a2d33dc8 100644 +--- a/src/login/logind-session-dbus.c ++++ b/src/login/logind-session-dbus.c +@@ -13,6 +13,7 @@ + #include "logind.h" + #include "signal-util.h" + #include "strv.h" ++#include "user-util.h" + #include "util.h" + + static int property_get_user( +@@ -182,6 +183,20 @@ int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_ + assert(message); + assert(s); + ++ r = bus_verify_polkit_async( ++ message, ++ CAP_SYS_ADMIN, ++ "org.freedesktop.login1.chvt", ++ NULL, ++ false, ++ UID_INVALID, ++ &s->manager->polkit_registry, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* Will call us back */ ++ + r = session_activate(s); + if (r < 0) + return r; +diff --git a/src/login/org.freedesktop.login1.policy b/src/login/org.freedesktop.login1.policy +index f1d1f956d3..83760e1580 100644 +--- a/src/login/org.freedesktop.login1.policy ++++ b/src/login/org.freedesktop.login1.policy +@@ -357,4 +357,14 @@ + + + ++ ++ Change Session ++ Authentication is required for changing the virtual terminal. ++ ++ auth_admin_keep ++ auth_admin_keep ++ yes ++ ++ ++ + diff --git a/SOURCES/0355-test-do-not-use-global-variable-to-pass-error.patch b/SOURCES/0355-test-do-not-use-global-variable-to-pass-error.patch new file mode 100644 index 0000000..89e07ba --- /dev/null +++ b/SOURCES/0355-test-do-not-use-global-variable-to-pass-error.patch @@ -0,0 +1,58 @@ +From 35e9e01b2879854a2adb8d571d0204990cad38d9 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 14 Sep 2018 13:25:02 +0900 +Subject: [PATCH] test: do not use global variable to pass error + +(cherry picked from commit 0013fac248a15be3acce84c17a65e3ae0377294b) + +Resolves: #1823767 +--- + test/TEST-21-SYSUSERS/test.sh | 1 + + test/test-functions | 8 +++++--- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/test/TEST-21-SYSUSERS/test.sh b/test/TEST-21-SYSUSERS/test.sh +index 73cbe10b69..b1049e720d 100755 +--- a/test/TEST-21-SYSUSERS/test.sh ++++ b/test/TEST-21-SYSUSERS/test.sh +@@ -15,6 +15,7 @@ prepare_testdir() { + for i in $1.initial-{passwd,group,shadow}; do + test -f $i && cp $i $TESTDIR/etc/${i#*.initial-} + done ++ return 0 + } + + preprocess() { +diff --git a/test/test-functions b/test/test-functions +index fe25a501da..da83891f46 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -449,7 +449,7 @@ EOF + } + + check_result_nspawn() { +- ret=1 ++ local ret=1 + [[ -e $TESTDIR/$1/testok ]] && ret=0 + [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR + cp -a $TESTDIR/$1/var/log/journal $TESTDIR +@@ -462,7 +462,7 @@ check_result_nspawn() { + + # can be overridden in specific test + check_result_qemu() { +- ret=1 ++ local ret=1 + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + [[ -e $TESTDIR/root/testok ]] && ret=0 +@@ -1527,7 +1527,9 @@ do_test() { + case $1 in + --run) + echo "TEST RUN: $TEST_DESCRIPTION" +- if test_run; then ++ test_run ++ ret=$? ++ if (( $ret == 0 )); then + echo "TEST RUN: $TEST_DESCRIPTION [OK]" + else + echo "TEST RUN: $TEST_DESCRIPTION [FAILED]" diff --git a/SOURCES/0356-test-install-libraries-required-by-tests.patch b/SOURCES/0356-test-install-libraries-required-by-tests.patch new file mode 100644 index 0000000..e14e742 --- /dev/null +++ b/SOURCES/0356-test-install-libraries-required-by-tests.patch @@ -0,0 +1,25 @@ +From 98e4c0d39a83a325038d0b39715fc534c7613bef Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 12 Sep 2018 18:19:45 +0900 +Subject: [PATCH] test: install libraries required by tests + +(cherry picked from commit e3d3dada248c5f30e2978840ca1f0a03a4675b53) + +Resolves: #1823767 +--- + test/test-functions | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/test-functions b/test/test-functions +index da83891f46..f7ca3ad975 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -420,7 +420,7 @@ get_ldpath() { + + install_missing_libraries() { + # install possible missing libraries +- for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/*; do ++ for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do + LD_LIBRARY_PATH=$(get_ldpath $i) inst_libs $i + done + } diff --git a/SOURCES/0357-test-introduce-install_zoneinfo.patch b/SOURCES/0357-test-introduce-install_zoneinfo.patch new file mode 100644 index 0000000..ba58935 --- /dev/null +++ b/SOURCES/0357-test-introduce-install_zoneinfo.patch @@ -0,0 +1,32 @@ +From 73781e52f331f816924232f96c7bfc92ee763e70 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 12 Sep 2018 18:20:31 +0900 +Subject: [PATCH] test: introduce install_zoneinfo() + +But it is not called by default. + +(cherry picked from commit 7d10ec1cda8fed20c36b16d2387f529583645cda) + +Resolves: #1823767 +--- + test/test-functions | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/test/test-functions b/test/test-functions +index f7ca3ad975..4d76ed1f82 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -628,6 +628,13 @@ install_keymaps() { + done + } + ++install_zoneinfo() { ++ for i in /usr/share/zoneinfo/{,*/,*/*/}*; do ++ [[ -f $i ]] || continue ++ inst $i ++ done ++} ++ + install_fonts() { + for i in \ + /usr/lib/kbd/consolefonts/eurlatgr* \ diff --git a/SOURCES/0358-test-replace-duplicated-Makefile-by-symbolic-link.patch b/SOURCES/0358-test-replace-duplicated-Makefile-by-symbolic-link.patch new file mode 100644 index 0000000..11032a9 --- /dev/null +++ b/SOURCES/0358-test-replace-duplicated-Makefile-by-symbolic-link.patch @@ -0,0 +1,151 @@ +From e39c4e6aee6c2ece5d9b51cc0e7a772016546f5a Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 13 Sep 2018 03:01:42 +0900 +Subject: [PATCH] test: replace duplicated Makefile by symbolic link + +(cherry picked from commit dd75c133d81f07c56c82ee4e7a80f391ffebd9ce) + +Resolves: #1823767 +--- + test/TEST-17-UDEV-WANTS/Makefile | 5 +---- + test/TEST-18-FAILUREACTION/Makefile | 5 +---- + test/TEST-19-DELEGATE/Makefile | 5 +---- + test/TEST-20-MAINPIDGAMES/Makefile | 5 +---- + test/TEST-21-SYSUSERS/Makefile | 5 +---- + test/TEST-22-TMPFILES/Makefile | 5 +---- + test/TEST-23-TYPE-EXEC/Makefile | 5 +---- + 7 files changed, 7 insertions(+), 28 deletions(-) + mode change 100644 => 120000 test/TEST-17-UDEV-WANTS/Makefile + mode change 100644 => 120000 test/TEST-18-FAILUREACTION/Makefile + mode change 100644 => 120000 test/TEST-19-DELEGATE/Makefile + mode change 100644 => 120000 test/TEST-20-MAINPIDGAMES/Makefile + mode change 100644 => 120000 test/TEST-21-SYSUSERS/Makefile + mode change 100644 => 120000 test/TEST-22-TMPFILES/Makefile + mode change 100644 => 120000 test/TEST-23-TYPE-EXEC/Makefile + +diff --git a/test/TEST-17-UDEV-WANTS/Makefile b/test/TEST-17-UDEV-WANTS/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-17-UDEV-WANTS/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-17-UDEV-WANTS/Makefile b/test/TEST-17-UDEV-WANTS/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-17-UDEV-WANTS/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-18-FAILUREACTION/Makefile b/test/TEST-18-FAILUREACTION/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-18-FAILUREACTION/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-18-FAILUREACTION/Makefile b/test/TEST-18-FAILUREACTION/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-18-FAILUREACTION/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-19-DELEGATE/Makefile b/test/TEST-19-DELEGATE/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-19-DELEGATE/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-19-DELEGATE/Makefile b/test/TEST-19-DELEGATE/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-19-DELEGATE/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-20-MAINPIDGAMES/Makefile b/test/TEST-20-MAINPIDGAMES/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-20-MAINPIDGAMES/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-20-MAINPIDGAMES/Makefile b/test/TEST-20-MAINPIDGAMES/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-20-MAINPIDGAMES/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-21-SYSUSERS/Makefile b/test/TEST-21-SYSUSERS/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-21-SYSUSERS/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-21-SYSUSERS/Makefile b/test/TEST-21-SYSUSERS/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-21-SYSUSERS/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-22-TMPFILES/Makefile b/test/TEST-22-TMPFILES/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-22-TMPFILES/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-22-TMPFILES/Makefile b/test/TEST-22-TMPFILES/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-22-TMPFILES/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-23-TYPE-EXEC/Makefile b/test/TEST-23-TYPE-EXEC/Makefile +deleted file mode 100644 +index 34d7cc6cdf..0000000000 +--- a/test/TEST-23-TYPE-EXEC/Makefile ++++ /dev/null +@@ -1,4 +0,0 @@ +-BUILD_DIR=$(shell ../../tools/find-build-dir.sh) +- +-all setup clean run: +- @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ +diff --git a/test/TEST-23-TYPE-EXEC/Makefile b/test/TEST-23-TYPE-EXEC/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-23-TYPE-EXEC/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file diff --git a/SOURCES/0359-test-add-paths-of-keymaps-in-install_keymaps.patch b/SOURCES/0359-test-add-paths-of-keymaps-in-install_keymaps.patch new file mode 100644 index 0000000..3ef2cdf --- /dev/null +++ b/SOURCES/0359-test-add-paths-of-keymaps-in-install_keymaps.patch @@ -0,0 +1,34 @@ +From 1599be6d3625700ded28108c0747b000448fa5dc Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 19 Sep 2018 10:54:16 +0900 +Subject: [PATCH] test: add paths of keymaps in install_keymaps() + +It seems that the paths of directories storing keymaps are changed. + +(cherry picked from commit 83a7051ee1edbfe8cd2278477d23083beb385409) + +Resolves: #1823767 +--- + test/test-functions | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/test/test-functions b/test/test-functions +index 4d76ed1f82..546928c516 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -619,10 +619,14 @@ install_pam() { + } + + install_keymaps() { ++ # The first three paths may be deprecated. ++ # It seems now the last two paths are used by many distributions. + for i in \ + /usr/lib/kbd/keymaps/include/* \ + /usr/lib/kbd/keymaps/i386/include/* \ +- /usr/lib/kbd/keymaps/i386/qwerty/us.*; do ++ /usr/lib/kbd/keymaps/i386/qwerty/us.* \ ++ /usr/lib/kbd/keymaps/legacy/include/* \ ++ /usr/lib/kbd/keymaps/legacy/i386/qwerty/us.*; do + [[ -f $i ]] || continue + inst $i + done diff --git a/SOURCES/0360-test-make-install_keymaps-optionally-install-more-ke.patch b/SOURCES/0360-test-make-install_keymaps-optionally-install-more-ke.patch new file mode 100644 index 0000000..52dff88 --- /dev/null +++ b/SOURCES/0360-test-make-install_keymaps-optionally-install-more-ke.patch @@ -0,0 +1,33 @@ +From 8dbd01f7018947fd15d00c25b1aa0ffc72278cb6 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 19 Sep 2018 10:54:28 +0900 +Subject: [PATCH] test: make install_keymaps() optionally install more keymaps + +(cherry picked from commit ad931fee506e1313e8a520ae0ecc1c8e275d9941) + +Resolves: #1823767 +--- + test/test-functions | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/test/test-functions b/test/test-functions +index 546928c516..0938e6e826 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -630,6 +630,16 @@ install_keymaps() { + [[ -f $i ]] || continue + inst $i + done ++ ++ # When it takes any argument, then install more keymaps. ++ if [[ -n $1 ]]; then ++ for i in \ ++ /usr/lib/kbd/keymaps/i386/*/* \ ++ /usr/lib/kbd/keymaps/legacy/i386/*/*; do ++ [[ -f $i ]] || continue ++ inst $i ++ done ++ fi + } + + install_zoneinfo() { diff --git a/SOURCES/0361-test-fs-util-skip-some-tests-when-running-in-unprivi.patch b/SOURCES/0361-test-fs-util-skip-some-tests-when-running-in-unprivi.patch new file mode 100644 index 0000000..d461fa5 --- /dev/null +++ b/SOURCES/0361-test-fs-util-skip-some-tests-when-running-in-unprivi.patch @@ -0,0 +1,47 @@ +From 15ab55eca3d1f7feb86e55bdc147069f36d198eb Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 14 Sep 2018 15:51:04 +0900 +Subject: [PATCH] test-fs-util: skip some tests when running in unprivileged + container + +(cherry picked from commit 9590065f37be040996f1c2b9a246b9952fdc0c0b) + +Resolves: #1823767 +--- + src/test/test-fs-util.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c +index 7b7990bb70..e3338ea440 100644 +--- a/src/test/test-fs-util.c ++++ b/src/test/test-fs-util.c +@@ -17,6 +17,7 @@ + #include "strv.h" + #include "user-util.h" + #include "util.h" ++#include "virt.h" + + static void test_chase_symlinks(void) { + _cleanup_free_ char *result = NULL; +@@ -468,6 +469,7 @@ static void test_touch_file(void) { + struct stat st; + const char *a; + usec_t test_mtime; ++ int r; + + test_uid = geteuid() == 0 ? 65534 : getuid(); + test_gid = geteuid() == 0 ? 65534 : getgid(); +@@ -517,7 +519,12 @@ static void test_touch_file(void) { + + if (geteuid() == 0) { + a = strjoina(p, "/cdev"); +- assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0); ++ r = mknod(a, 0775 | S_IFCHR, makedev(0, 0)); ++ if (r < 0 && errno == EPERM && detect_container() > 0) { ++ log_notice("Running in unprivileged container? Skipping remaining tests in %s", __func__); ++ return; ++ } ++ assert_se(r >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); diff --git a/SOURCES/0362-test-process-util-skip-several-verifications-when-ru.patch b/SOURCES/0362-test-process-util-skip-several-verifications-when-ru.patch new file mode 100644 index 0000000..c3edf03 --- /dev/null +++ b/SOURCES/0362-test-process-util-skip-several-verifications-when-ru.patch @@ -0,0 +1,39 @@ +From b550cc33e762fa209b0740f1874f75ef780b8d90 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Thu, 20 Sep 2018 16:08:38 +0900 +Subject: [PATCH] test-process-util: skip several verifications when running in + unprivileged container + +(cherry picked from commit 767eab47501b06327a0e6030e5c54860a3fc427f) + +Resolves: #1823767 +--- + src/test/test-process-util.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c +index fd4d17408d..26e3247993 100644 +--- a/src/test/test-process-util.c ++++ b/src/test/test-process-util.c +@@ -394,12 +394,17 @@ static void test_rename_process_now(const char *p, int ret) { + log_info("comm = <%s>", comm); + assert_se(strneq(comm, p, TASK_COMM_LEN-1)); + +- assert_se(get_process_cmdline(0, 0, false, &cmdline) >= 0); ++ r = get_process_cmdline(0, 0, false, &cmdline); ++ assert_se(r >= 0); + /* we cannot expect cmdline to be renamed properly without privileges */ + if (geteuid() == 0) { +- log_info("cmdline = <%s>", cmdline); +- assert_se(strneq(p, cmdline, STRLEN("test-process-util"))); +- assert_se(startswith(p, cmdline)); ++ if (r == 0 && detect_container() > 0) ++ log_info("cmdline = <%s> (not verified, Running in unprivileged container?)", cmdline); ++ else { ++ log_info("cmdline = <%s>", cmdline); ++ assert_se(strneq(p, cmdline, STRLEN("test-process-util"))); ++ assert_se(startswith(p, cmdline)); ++ } + } else + log_info("cmdline = <%s> (not verified)", cmdline); + } diff --git a/SOURCES/0363-test-execute-also-check-python3-is-installed-or-not.patch b/SOURCES/0363-test-execute-also-check-python3-is-installed-or-not.patch new file mode 100644 index 0000000..7bfe287 --- /dev/null +++ b/SOURCES/0363-test-execute-also-check-python3-is-installed-or-not.patch @@ -0,0 +1,59 @@ +From a3a3d861496b8c0d061c6ba21278d0326c50f37d Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 12 Sep 2018 18:18:33 +0900 +Subject: [PATCH] test-execute: also check python3 is installed or not + +(cherry picked from commit 738c74d7b163ea18e3c68115c3ed8ceed166cbf7) + +Resolves: #1823767 +--- + src/test/test-execute.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index 6c22995b1e..af64427bc7 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -317,6 +317,8 @@ static void test_exec_temporaryfilesystem(Manager *m) { + + static void test_exec_systemcallfilter(Manager *m) { + #if HAVE_SECCOMP ++ int r; ++ + if (!is_seccomp_available()) { + log_notice("Seccomp not available, skipping %s", __func__); + return; +@@ -326,6 +328,13 @@ static void test_exec_systemcallfilter(Manager *m) { + test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED); + test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED); + test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED); ++ ++ r = find_binary("python3", NULL); ++ if (r < 0) { ++ log_notice_errno(r, "Skipping remaining tests in %s, could not find python3 binary: %m", __func__); ++ return; ++ } ++ + test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED); + test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED); + #endif +@@ -333,11 +342,19 @@ static void test_exec_systemcallfilter(Manager *m) { + + static void test_exec_systemcallerrornumber(Manager *m) { + #if HAVE_SECCOMP ++ int r; ++ + if (!is_seccomp_available()) { + log_notice("Seccomp not available, skipping %s", __func__); + return; + } + ++ r = find_binary("python3", NULL); ++ if (r < 0) { ++ log_notice_errno(r, "Skipping %s, could not find python3 binary: %m", __func__); ++ return; ++ } ++ + test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED); + test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED); + #endif diff --git a/SOURCES/0364-test-execute-skip-several-tests-when-running-in-cont.patch b/SOURCES/0364-test-execute-skip-several-tests-when-running-in-cont.patch new file mode 100644 index 0000000..472d8ff --- /dev/null +++ b/SOURCES/0364-test-execute-skip-several-tests-when-running-in-cont.patch @@ -0,0 +1,42 @@ +From fb66f2dbf1d228adc6f15edbbdf0ce53eb3be982 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 14 Sep 2018 15:47:42 +0900 +Subject: [PATCH] test-execute: skip several tests when running in container + +(cherry picked from commit 642d1a6d6e98204ade25816bcc429cb67df92a29) + +Resolves: #1823767 +--- + src/test/test-execute.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index af64427bc7..637ffe96bb 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -616,14 +616,24 @@ static void test_exec_privatenetwork(Manager *m) { + + static void test_exec_oomscoreadjust(Manager *m) { + test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED); ++ ++ if (detect_container() > 0) { ++ log_notice("Testing in container, skipping remaining tests in %s", __func__); ++ return; ++ } + test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED); + } + + static void test_exec_ioschedulingclass(Manager *m) { + test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED); + test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED); +- test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED); + test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED); ++ ++ if (detect_container() > 0) { ++ log_notice("Testing in container, skipping remaining tests in %s", __func__); ++ return; ++ } ++ test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED); + } + + static void test_exec_unsetenvironment(Manager *m) { diff --git a/SOURCES/0365-test-introduce-test_is_running_from_builddir.patch b/SOURCES/0365-test-introduce-test_is_running_from_builddir.patch new file mode 100644 index 0000000..c572849 --- /dev/null +++ b/SOURCES/0365-test-introduce-test_is_running_from_builddir.patch @@ -0,0 +1,70 @@ +From b69552ccc0e33f713ae3a2baf1b0173cf221507d Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 11 Sep 2018 09:17:22 +0900 +Subject: [PATCH] test: introduce test_is_running_from_builddir() + +(cherry picked from commit 8cb10a4f4dabc508a04f76ea55f23ef517881b61) + +Resolves: #1823767 +--- + src/shared/tests.c | 23 ++++++++++++++++++++--- + src/shared/tests.h | 1 + + 2 files changed, 21 insertions(+), 3 deletions(-) + +diff --git a/src/shared/tests.c b/src/shared/tests.c +index b10343650f..c77eb00924 100644 +--- a/src/shared/tests.c ++++ b/src/shared/tests.c +@@ -19,6 +19,24 @@ char* setup_fake_runtime_dir(void) { + return p; + } + ++bool test_is_running_from_builddir(char **exedir) { ++ _cleanup_free_ char *s = NULL; ++ bool r; ++ ++ /* Check if we're running from the builddir. Optionally, this returns ++ * the path to the directory where the binary is located. */ ++ ++ assert_se(readlink_and_make_absolute("/proc/self/exe", &s) >= 0); ++ r = path_startswith(s, ABS_BUILD_DIR); ++ ++ if (exedir) { ++ dirname(s); ++ *exedir = TAKE_PTR(s); ++ } ++ ++ return r; ++} ++ + const char* get_testdata_dir(const char *suffix) { + const char *env; + /* convenience: caller does not need to free result */ +@@ -35,14 +53,13 @@ const char* get_testdata_dir(const char *suffix) { + strncpy(testdir, env, sizeof(testdir) - 1); + } else { + _cleanup_free_ char *exedir = NULL; +- assert_se(readlink_and_make_absolute("/proc/self/exe", &exedir) >= 0); + + /* Check if we're running from the builddir. If so, use the compiled in path. */ +- if (path_startswith(exedir, ABS_BUILD_DIR)) ++ if (test_is_running_from_builddir(&exedir)) + assert_se(snprintf(testdir, sizeof(testdir), "%s/test", ABS_SRC_DIR) > 0); + else + /* Try relative path, according to the install-test layout */ +- assert_se(snprintf(testdir, sizeof(testdir), "%s/testdata", dirname(exedir)) > 0); ++ assert_se(snprintf(testdir, sizeof(testdir), "%s/testdata", exedir) > 0); + + /* test this without the suffix, as it may contain a glob */ + if (access(testdir, F_OK) < 0) { +diff --git a/src/shared/tests.h b/src/shared/tests.h +index cad21169f8..7f45c32d32 100644 +--- a/src/shared/tests.h ++++ b/src/shared/tests.h +@@ -2,5 +2,6 @@ + #pragma once + + char* setup_fake_runtime_dir(void); ++bool test_is_running_from_builddir(char **exedir); + const char* get_testdata_dir(const char *suffix); + void test_setup_logging(int level); diff --git a/SOURCES/0366-test-make-test-catalog-relocatable.patch b/SOURCES/0366-test-make-test-catalog-relocatable.patch new file mode 100644 index 0000000..7af676f --- /dev/null +++ b/SOURCES/0366-test-make-test-catalog-relocatable.patch @@ -0,0 +1,103 @@ +From ac476ab0ce970e4a269fee34a15e24f6b20962b7 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 11 Sep 2018 09:18:33 +0900 +Subject: [PATCH] test: make test-catalog relocatable + +Fixes #10045. + +(cherry picked from commit d9b6baa69968132d33e4ad8627c7fe0bd527c859) + +Resolves: #1823767 +--- + catalog/meson.build | 1 - + src/journal/test-catalog.c | 27 +++++++++++++++++++-------- + src/test/meson.build | 2 +- + 3 files changed, 20 insertions(+), 10 deletions(-) + +diff --git a/catalog/meson.build b/catalog/meson.build +index 1b13150894..3db8e390f2 100644 +--- a/catalog/meson.build ++++ b/catalog/meson.build +@@ -17,7 +17,6 @@ in_files = ''' + + support_url = get_option('support-url') + support_sed = 's~%SUPPORT_URL%~@0@~'.format(support_url) +-build_catalog_dir = meson.current_build_dir() + + foreach file : in_files + custom_target( +diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c +index 8eae993780..0c4da29f31 100644 +--- a/src/journal/test-catalog.c ++++ b/src/journal/test-catalog.c +@@ -14,14 +14,13 @@ + #include "fileio.h" + #include "log.h" + #include "macro.h" ++#include "path-util.h" + #include "string-util.h" ++#include "strv.h" ++#include "tests.h" + #include "util.h" + +-static const char *catalog_dirs[] = { +- CATALOG_DIR, +- NULL, +-}; +- ++static char** catalog_dirs = NULL; + static const char *no_catalog_dirs[] = { + "/bin/hopefully/with/no/catalog", + NULL +@@ -167,8 +166,8 @@ static void test_catalog_update(const char *database) { + assert_se(r == 0); + + /* Make sure that we at least have some files loaded or the +- catalog_list below will fail. */ +- r = catalog_update(database, NULL, catalog_dirs); ++ * catalog_list below will fail. */ ++ r = catalog_update(database, NULL, (const char * const *) catalog_dirs); + assert_se(r == 0); + } + +@@ -202,14 +201,26 @@ static void test_catalog_file_lang(void) { + + int main(int argc, char *argv[]) { + _cleanup_(unlink_tempfilep) char database[] = "/tmp/test-catalog.XXXXXX"; +- _cleanup_free_ char *text = NULL; ++ _cleanup_free_ char *text = NULL, *catalog_dir = NULL; + int r; + + setlocale(LC_ALL, "de_DE.UTF-8"); + ++ log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + ++ /* If test-catalog is located at the build directory, then use catalogs in that. ++ * If it is not, e.g. installed by systemd-tests package, then use installed catalogs. */ ++ if (test_is_running_from_builddir(NULL)) { ++ assert_se(catalog_dir = path_join(NULL, ABS_BUILD_DIR, "catalog")); ++ catalog_dirs = STRV_MAKE(catalog_dir); ++ } else ++ catalog_dirs = STRV_MAKE(CATALOG_DIR); ++ ++ assert_se(access(catalog_dirs[0], F_OK) >= 0); ++ log_notice("Using catalog directory '%s'", catalog_dirs[0]); ++ + test_catalog_file_lang(); + + test_catalog_import_invalid(); +diff --git a/src/test/meson.build b/src/test/meson.build +index 4259421f98..ead000e30c 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -766,7 +766,7 @@ tests += [ + [threads, + libxz, + liblz4], +- '', '', '-DCATALOG_DIR="@0@"'.format(build_catalog_dir)], ++ '', '', '-DCATALOG_DIR="@0@"'.format(catalogdir)], + + [['src/journal/test-compress.c'], + [libjournal_core, diff --git a/SOURCES/0367-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch b/SOURCES/0367-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch new file mode 100644 index 0000000..fff67d8 --- /dev/null +++ b/SOURCES/0367-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch @@ -0,0 +1,137 @@ +From 7ba4a6f5a02f6142d0e28fd475fd008532fb1083 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 5 Mar 2019 13:50:28 +0100 +Subject: [PATCH] test: parallelize tasks in TEST-24-UNIT-TESTS + +(cherry picked from commit 2f2a0454efd07644a4e0ccb3f00f1db2d7043391) + +Related: #1823767 +--- + test/TEST-24-UNIT-TESTS/test.sh | 2 + + test/TEST-24-UNIT-TESTS/testsuite.sh | 97 +++++++++++++++++++++------- + 2 files changed, 77 insertions(+), 22 deletions(-) + +diff --git a/test/TEST-24-UNIT-TESTS/test.sh b/test/TEST-24-UNIT-TESTS/test.sh +index 014ee52277..fc8c89fe0a 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 ++NPROC=$(nproc) ++MAX_QUEUE_SIZE=${NPROC:-2} ++IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*)) ++ ++# Check & report test results ++# Arguments: ++# $1: test path ++# $2: test exit code ++function report_result() { ++ if [[ $# -ne 2 ]]; then ++ echo >&2 "check_result: missing arguments" ++ exit 1 ++ fi ++ ++ local name="${1##*/}" ++ local ret=$2 ++ ++ if [[ $ret -ne 0 && $ret != 77 ]]; then ++ echo "$name failed with $ret" ++ echo "$name" >> /failed-tests ++ { ++ echo "--- $name begin ---" ++ cat "/$name.log" ++ echo "--- $name end ---" ++ } >> /failed ++ elif [[ $ret == 77 ]]; then ++ echo "$name skipped" ++ echo "$name" >> /skipped-tests ++ { ++ echo "--- $name begin ---" ++ cat "/$name.log" ++ echo "--- $name end ---" ++ } >> /skipped + else +- echo "$NAME OK" +- echo $NAME >> /testok ++ 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 + +- systemd-cat echo "--- $NAME ---" +- systemd-cat cat /$NAME.log ++# 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/SOURCES/0368-test-try-to-determine-QEMU_SMP-dynamically.patch b/SOURCES/0368-test-try-to-determine-QEMU_SMP-dynamically.patch new file mode 100644 index 0000000..a53b7b2 --- /dev/null +++ b/SOURCES/0368-test-try-to-determine-QEMU_SMP-dynamically.patch @@ -0,0 +1,41 @@ +From fe6e09aa0931112e7e3750801858c66129c7a3a8 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 5 Mar 2019 16:08:00 +0100 +Subject: [PATCH] test: try to determine QEMU_SMP dynamically + +If the QEMU_SMP value has not been explicitly set, try to determine it +from the number of online CPUs using the nproc utility. If this approach +fails, fall back to the default value QEMU_SMP=1. + +This change should significantly help when running integration tests +under QEMU on multicore systems. + +(cherry picked from commit 5bfb2a93a4a36bba0d24199553dcda6e560cbb75) + +Related: #1823767 +--- + test/test-functions | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/test/test-functions b/test/test-functions +index 0938e6e826..3f1c327f3c 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -120,7 +120,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 + diff --git a/SOURCES/0369-test-store-coredumps-in-journal.patch b/SOURCES/0369-test-store-coredumps-in-journal.patch new file mode 100644 index 0000000..ca9f771 --- /dev/null +++ b/SOURCES/0369-test-store-coredumps-in-journal.patch @@ -0,0 +1,31 @@ +From 8df2d39a562416e1218e3ff191f3f3af1f9d4844 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Tue, 13 Aug 2019 00:14:54 +0200 +Subject: [PATCH] test: store coredumps in journal + +To make debugging much easier, especially for crashes in tests under +QEMU, let's store the entire coredump bundle in the systemd journal, +which is usually kept around by various CIs. Right now, we usually end +up with a journal, but without the coredump itself, which is pretty +useless. + +(cherry picked from commit 215bffe1b8d7cb72fe9f72ed53682d52d5c2a9c5) + +Related: #1823767 +--- + test/test-functions | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/test/test-functions b/test/test-functions +index 3f1c327f3c..7c4230b078 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -420,6 +420,8 @@ install_systemd() { + + # enable debug logging in PID1 + echo LogLevel=debug >> $initdir/etc/systemd/system.conf ++ # store coredumps in journal ++ echo Storage=journal >> $initdir/etc/systemd/coredump.conf + } + + get_ldpath() { diff --git a/SOURCES/0370-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch b/SOURCES/0370-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch new file mode 100644 index 0000000..340e056 --- /dev/null +++ b/SOURCES/0370-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch @@ -0,0 +1,62 @@ +From 82156850f6642a363aa2ff06677ad089a460104e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Nov 2019 09:46:00 +0100 +Subject: [PATCH] pid1: add new kernel cmdline arg systemd.cpu_affinity= + +Let's allow configuration of the CPU affinity via the kernel cmdline, +overriding CPUAffinity= in /etc/systemd/system.conf + +Prompted by: + +https://lists.freedesktop.org/archives/systemd-devel/2019-November/043754.html + +(cherry picked from commit 68d58f38693e586b5ce5785274f8e42a79625196) + +Resolves: #1812894 +--- + man/kernel-command-line.xml | 11 +++++++++++ + src/core/main.c | 9 +++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml +index 0545f9d84b..4d8cb4e50e 100644 +--- a/man/kernel-command-line.xml ++++ b/man/kernel-command-line.xml +@@ -366,6 +366,17 @@ + + + ++ ++ systemd.cpu_affinity= ++ ++ ++ Overrides the CPU affinity mask for the service manager and the default for all child ++ processes it forks. This takes precedence over CPUAffinity=, see ++ systemd-system.conf5 ++ for details. ++ ++ ++ + + modules_load= + rd.modules_load= +diff --git a/src/core/main.c b/src/core/main.c +index 45d09b1e11..9f238a8430 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -472,6 +472,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + if (arg_default_timeout_start_usec <= 0) + arg_default_timeout_start_usec = USEC_INFINITY; + ++ } else if (proc_cmdline_key_streq(key, "systemd.cpu_affinity")) { ++ ++ if (proc_cmdline_value_missing(key, value)) ++ return 0; ++ ++ r = parse_cpu_set(value, &arg_cpu_affinity); ++ if (r < 0) ++ log_warning_errno(r, "Failed to parse CPU affinity mask '%s', ignoring: %m", value); ++ + } else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) { + + if (proc_cmdline_value_missing(key, value)) diff --git a/SOURCES/0371-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch b/SOURCES/0371-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch new file mode 100644 index 0000000..e267fe5 --- /dev/null +++ b/SOURCES/0371-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch @@ -0,0 +1,57 @@ +From f60e89ea4c38c11a9d0c1e642c0a78faa32aca56 Mon Sep 17 00:00:00 2001 +From: Joerg Steffens +Date: Tue, 21 Nov 2017 12:21:49 +0100 +Subject: [PATCH] udev-rules: make tape-changers also apprear in + /dev/tape/by-path/ + +It is important to be able to access tape changer ("Medium Changers") by +persistant name. +While tape devices can be accessed via /dev/tape/by-id/ and +/dev/tape/by-path/, tape-changers could only be accessed by +/dev/tape/by-id/. +However, in some cases, especially when accessing Amazon Webservice +Storage Gateway VTLs (or accessing iSCSI VTLs in general?) this does not +work, as all tape devices and the tape changer have the same ENV{ID_SERIAL}. +The results is, that only the last device is available in +/dev/tape/by-id/, as the former devices have been overwritten. + +As this behavior is hard to change without breaking consistentcy, +this additional device in /dev/tape/by-path/ can be used to access the medium changes. +The tape devices can also be accessed by this path. + +The content of the directory will now look like: + + # SCSI tape device, rewind (unchanged) + /dev/tape/by-path/$env{ID_PATH} -> ../../st* + + # SCSI tape device, no-rewind (unchanged) + /dev/tape/by-path/$env{ID_PATH}-nst -> ../../nst* + + # SCSI tape changer device (newly added) + /dev/tape/by-path/$env{ID_PATH}-changer -> ../../sg* + +Tape devices and tape changer have different ID_PATHs. +SCSI tape changer get the suffix "-changer" +to make them better distinguishable from tape devices. + +(cherry picked from commit 7f8ddf96a25162f06bd94a684cf700c128d18142) + +Resolves: #1820112 +--- + rules/60-persistent-storage-tape.rules | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/rules/60-persistent-storage-tape.rules b/rules/60-persistent-storage-tape.rules +index b604864ee8..0575f308df 100644 +--- a/rules/60-persistent-storage-tape.rules ++++ b/rules/60-persistent-storage-tape.rules +@@ -9,6 +9,9 @@ ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_t + SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ + SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" + ++SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{builtin}="path_id", \ ++ SYMLINK+="tape/by-path/$env{ID_PATH}-changer" ++ + SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" + + KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" diff --git a/SOURCES/0372-man-be-clearer-that-.timer-time-expressions-need-to-.patch b/SOURCES/0372-man-be-clearer-that-.timer-time-expressions-need-to-.patch new file mode 100644 index 0000000..8e72683 --- /dev/null +++ b/SOURCES/0372-man-be-clearer-that-.timer-time-expressions-need-to-.patch @@ -0,0 +1,75 @@ +From b8af9fd65b697e9bb77a32d1a6a70367814aaed5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Apr 2019 17:30:45 +0200 +Subject: [PATCH] man: be clearer that .timer time expressions need to be reset + to override them + +let's be clearer about the overriding concept for OnCalendar= settings. + +Prompted by this thread: + +https://lists.freedesktop.org/archives/systemd-devel/2019-March/042351.html +(cherry picked from commit 58031d99c6320855b86f4890baa9165597e3d841) + +Resolves: #1816908 +--- + man/systemd.timer.xml | 31 ++++++++++++++++++------------- + 1 file changed, 18 insertions(+), 13 deletions(-) + +diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml +index 44b257c745..ebc1df89f1 100644 +--- a/man/systemd.timer.xml ++++ b/man/systemd.timer.xml +@@ -125,12 +125,12 @@ + to when the unit the timer is activating was last + deactivated. + +- 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. ++ 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 +@@ -145,13 +145,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 +@@ -175,7 +174,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. + + + diff --git a/SOURCES/0373-Add-support-for-opening-files-for-appending.patch b/SOURCES/0373-Add-support-for-opening-files-for-appending.patch new file mode 100644 index 0000000..fbfa0e8 --- /dev/null +++ b/SOURCES/0373-Add-support-for-opening-files-for-appending.patch @@ -0,0 +1,312 @@ +From 2808e53f785e9ca7fdab286678e784b661b4c185 Mon Sep 17 00:00:00 2001 +From: Zsolt Dollenstein +Date: Tue, 3 Jul 2018 12:22:29 -0700 +Subject: [PATCH] Add support for opening files for appending + +Addresses part of #8983 + +(cherry picked from commit 566b7d23eb747e9c5a74e5647693077b52395fc5) + +Resolves: #1809175 +--- + man/systemd.exec.xml | 16 ++++++---- + src/core/dbus-execute.c | 30 ++++++++++++++----- + src/core/execute.c | 20 ++++++++++--- + src/core/execute.h | 1 + + src/core/load-fragment.c | 11 +++++++ + src/core/main.c | 4 +-- + src/test/test-execute.c | 10 +++++++ + test/meson.build | 2 ++ + .../exec-standardoutput-append.service | 13 ++++++++ + .../exec-standardoutput-file.service | 13 ++++++++ + 10 files changed, 101 insertions(+), 19 deletions(-) + create mode 100644 test/test-execute/exec-standardoutput-append.service + create mode 100644 test/test-execute/exec-standardoutput-file.service + +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index bdaed68162..e2a5ede968 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -1792,8 +1792,8 @@ SystemCallErrorNumber=EPERM + of , , , , + , , , + , , +- , or +- . ++ , , ++ or. + + duplicates the file descriptor of standard input for standard output. + +@@ -1824,11 +1824,17 @@ SystemCallErrorNumber=EPERM + + The option may be used to connect a specific file + system object to standard output. The semantics are similar to the same option of +- StandardInput=, see above. If standard input and output are directed to the same file path, +- it is opened only once, for reading as well as writing and duplicated. This is particular useful when the +- specified path refers to an AF_UNIX socket in the file system, as in that case only a ++ StandardInput=, see above. If path refers to a regular file ++ on the filesystem, it is opened (created if it doesn't exist yet) for writing at the beginning of the file, ++ but without truncating it. ++ If standard input and output are directed to the same file path, it is opened only once, for reading as well ++ as writing and duplicated. This is particularly useful when the specified path refers to an ++ AF_UNIX socket in the file system, as in that case only a + single stream connection is created for both input and output. + ++ is similar to above, but it opens the file in append mode. ++ + connects standard output to a socket acquired via socket activation. The + semantics are similar to the same option of StandardInput=, see above. + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index e7c0b893d1..f9527e56b2 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1772,7 +1772,10 @@ int bus_exec_context_set_transient_property( + + return 1; + +- } else if (STR_IN_SET(name, "StandardInputFile", "StandardOutputFile", "StandardErrorFile")) { ++ } else if (STR_IN_SET(name, ++ "StandardInputFile", ++ "StandardOutputFile", "StandardOutputFileToCreate", "StandardOutputFileToAppend", ++ "StandardErrorFile", "StandardErrorFileToCreate", "StandardErrorFileToAppend")) { + const char *s; + + r = sd_bus_message_read(message, "s", &s); +@@ -1796,23 +1799,34 @@ int bus_exec_context_set_transient_property( + c->std_input = EXEC_INPUT_FILE; + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s); + +- } else if (streq(name, "StandardOutputFile")) { ++ } else if (STR_IN_SET(name, "StandardOutputFile", "StandardOutputFileToAppend")) { + r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s)); + if (r < 0) + return r; + +- c->std_output = EXEC_OUTPUT_FILE; +- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s); +- ++ if (streq(name, "StandardOutputFile")) { ++ c->std_output = EXEC_OUTPUT_FILE; ++ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s); ++ } else { ++ assert(streq(name, "StandardOutputFileToAppend")); ++ c->std_output = EXEC_OUTPUT_FILE_APPEND; ++ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=append:%s", s); ++ } + } else { +- assert(streq(name, "StandardErrorFile")); ++ assert(STR_IN_SET(name, "StandardErrorFile", "StandardErrorFileToAppend")); + + r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s)); + if (r < 0) + return r; + +- c->std_error = EXEC_OUTPUT_FILE; +- unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardError=file:%s", s); ++ if (streq(name, "StandardErrorFile")) { ++ c->std_error = EXEC_OUTPUT_FILE; ++ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=file:%s", s); ++ } else { ++ assert(streq(name, "StandardErrorFileToAppend")); ++ c->std_error = EXEC_OUTPUT_FILE_APPEND; ++ unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardOutput=append:%s", s); ++ } + } + } + +diff --git a/src/core/execute.c b/src/core/execute.c +index f012023224..3c54ac1110 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -89,6 +89,7 @@ + #include "strv.h" + #include "syslog-util.h" + #include "terminal-util.h" ++#include "umask-util.h" + #include "unit.h" + #include "user-util.h" + #include "util.h" +@@ -675,9 +676,10 @@ static int setup_output( + (void) fd_nonblock(named_iofds[fileno], false); + return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno; + +- case EXEC_OUTPUT_FILE: { ++ case EXEC_OUTPUT_FILE: ++ case EXEC_OUTPUT_FILE_APPEND: { + bool rw; +- int fd; ++ int fd, flags; + + assert(context->stdio_file[fileno]); + +@@ -687,11 +689,16 @@ static int setup_output( + if (rw) + return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno; + +- fd = acquire_path(context->stdio_file[fileno], O_WRONLY, 0666 & ~context->umask); ++ flags = O_WRONLY; ++ if (o == EXEC_OUTPUT_FILE_APPEND) ++ flags |= O_APPEND; ++ ++ fd = acquire_path(context->stdio_file[fileno], flags, 0666 & ~context->umask); ++ + if (fd < 0) + return fd; + +- return move_fd(fd, fileno, false); ++ return move_fd(fd, fileno, 0); + } + + default: +@@ -4168,8 +4175,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { + fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]); + if (c->std_output == EXEC_OUTPUT_FILE) + fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]); ++ if (c->std_output == EXEC_OUTPUT_FILE_APPEND) ++ fprintf(f, "%sStandardOutputFileToAppend: %s\n", prefix, c->stdio_file[STDOUT_FILENO]); + if (c->std_error == EXEC_OUTPUT_FILE) + fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]); ++ if (c->std_error == EXEC_OUTPUT_FILE_APPEND) ++ fprintf(f, "%sStandardErrorFileToAppend: %s\n", prefix, c->stdio_file[STDERR_FILENO]); + + if (c->tty_path) + fprintf(f, +@@ -5111,6 +5122,7 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { + [EXEC_OUTPUT_SOCKET] = "socket", + [EXEC_OUTPUT_NAMED_FD] = "fd", + [EXEC_OUTPUT_FILE] = "file", ++ [EXEC_OUTPUT_FILE_APPEND] = "append", + }; + + DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); +diff --git a/src/core/execute.h b/src/core/execute.h +index 2266355962..86c1cee84c 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -57,6 +57,7 @@ typedef enum ExecOutput { + EXEC_OUTPUT_SOCKET, + EXEC_OUTPUT_NAMED_FD, + EXEC_OUTPUT_FILE, ++ EXEC_OUTPUT_FILE_APPEND, + _EXEC_OUTPUT_MAX, + _EXEC_OUTPUT_INVALID = -1 + } ExecOutput; +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 2082166afb..9b2724307d 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1016,6 +1016,17 @@ int config_parse_exec_output( + + eo = EXEC_OUTPUT_FILE; + ++ } else if ((n = startswith(rvalue, "append:"))) { ++ ++ r = unit_full_printf(u, n, &resolved); ++ if (r < 0) ++ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", n); ++ ++ r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE | PATH_CHECK_FATAL, unit, filename, line, lvalue); ++ if (r < 0) ++ return -ENOEXEC; ++ ++ eo = EXEC_OUTPUT_FILE_APPEND; + } else { + eo = exec_output_from_string(rvalue); + if (eo < 0) { +diff --git a/src/core/main.c b/src/core/main.c +index 9f238a8430..25536054b3 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -620,8 +620,8 @@ static int config_parse_output_restricted( + return 0; + } + +- if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE)) { +- log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file: are not supported as defaults, ignoring: %s", rvalue); ++ if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) { ++ log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue); + return 0; + } + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index 637ffe96bb..0f8dc883b1 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -651,6 +651,14 @@ static void test_exec_standardinput(Manager *m) { + test(m, "exec-standardinput-file.service", 0, CLD_EXITED); + } + ++static void test_exec_standardoutput(Manager *m) { ++ test(m, "exec-standardoutput-file.service", 0, CLD_EXITED); ++} ++ ++static void test_exec_standardoutput_append(Manager *m) { ++ test(m, "exec-standardoutput-append.service", 0, CLD_EXITED); ++} ++ + static int run_tests(UnitFileScope scope, const test_function_t *tests) { + const test_function_t *test = NULL; + _cleanup_(manager_freep) Manager *m = NULL; +@@ -698,6 +706,8 @@ int main(int argc, char *argv[]) { + test_exec_restrictnamespaces, + test_exec_runtimedirectory, + test_exec_standardinput, ++ test_exec_standardoutput, ++ test_exec_standardoutput_append, + test_exec_supplementarygroups, + test_exec_systemcallerrornumber, + test_exec_systemcallfilter, +diff --git a/test/meson.build b/test/meson.build +index fb9f2cdb9b..4d1c51048c 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -115,6 +115,8 @@ test_data_files = ''' + test-execute/exec-specifier@.service + test-execute/exec-standardinput-data.service + test-execute/exec-standardinput-file.service ++ test-execute/exec-standardoutput-file.service ++ test-execute/exec-standardoutput-append.service + test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service + test-execute/exec-supplementarygroups-multiple-groups-withgid.service + test-execute/exec-supplementarygroups-multiple-groups-withuid.service +diff --git a/test/test-execute/exec-standardoutput-append.service b/test/test-execute/exec-standardoutput-append.service +new file mode 100644 +index 0000000000..8983bb056b +--- /dev/null ++++ b/test/test-execute/exec-standardoutput-append.service +@@ -0,0 +1,13 @@ ++[Unit] ++Description=Test for StandardOutput=append: ++ ++[Service] ++ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output' ++ExecStartPre=sh -c 'printf "hello\nhello\n" > /tmp/test-exec-standardoutput-expected' ++StandardInput=data ++StandardInputText=hello ++StandardOutput=append:/tmp/test-exec-standardoutput-output ++StandardError=null ++ExecStart=cat ++ExecStart=cmp /tmp/test-exec-standardoutput-output /tmp/test-exec-standardoutput-expected ++Type=oneshot +diff --git a/test/test-execute/exec-standardoutput-file.service b/test/test-execute/exec-standardoutput-file.service +new file mode 100644 +index 0000000000..71e2604b94 +--- /dev/null ++++ b/test/test-execute/exec-standardoutput-file.service +@@ -0,0 +1,13 @@ ++[Unit] ++Description=Test for StandardOutput=file: ++ ++[Service] ++ExecStartPre=sh -c 'printf "nooo\nhello\n" > /tmp/test-exec-standardoutput-output' ++ExecStartPre=sh -c 'printf "hello\nello\n" > /tmp/test-exec-standardoutput-expected' ++StandardInput=data ++StandardInputText=hello ++StandardOutput=file:/tmp/test-exec-standardoutput-output ++StandardError=null ++ExecStart=cat ++ExecStart=cmp /tmp/test-exec-standardoutput-expected /tmp/test-exec-standardoutput-output ++Type=oneshot diff --git a/SOURCES/0374-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch b/SOURCES/0374-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch new file mode 100644 index 0000000..52971de --- /dev/null +++ b/SOURCES/0374-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch @@ -0,0 +1,36 @@ +From 8ee1465520ad49892a0a378626ef93abc03f4d4e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 5 Mar 2019 18:57:53 +0100 +Subject: [PATCH] nspawn: move payload to sub-cgroup first, then sync cgroup + trees + +if we sync the legacy and unified trees before moving to the right +subcgroup then ultimately the cgroup paths in the hierarchies will be +out-of-sync... Hence, let's move the payload first, and sync then. + +Addresses: https://github.com/systemd/systemd/pull/9762#issuecomment-441187979 +(cherry picked from commit 27da7ef0d09e00eae821f3ef26e1a666fe7aa087) + +Resolves: #1837094 +--- + src/nspawn/nspawn.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index 08255b5724..8cb7591f0e 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -3960,11 +3960,11 @@ static int run(int master, + } 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; + diff --git a/SOURCES/0375-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch b/SOURCES/0375-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch new file mode 100644 index 0000000..1914ad5 --- /dev/null +++ b/SOURCES/0375-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch @@ -0,0 +1,31 @@ +From f4a34d97bd7e1564a51f590df591cb31a1a3f333 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 17 Sep 2018 07:12:38 +0000 +Subject: [PATCH] nspawn: chown() the legacy hierarchy when it's used in a + container + +This is a follow-up to 720f0a2f3c928cc9379501a52146be9fbb4d9be2. + +Closes https://github.com/systemd/systemd/issues/10026 +Closes https://github.com/systemd/systemd/issues/9563 + +(cherry picked from commit 89f180201cd8c0f3ce5cb6e8dd7e2b3cbcf71527) + +Resolves: 1837094 +--- + src/nspawn/nspawn-cgroup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c +index d8a39a6959..a231622e29 100644 +--- a/src/nspawn/nspawn-cgroup.c ++++ b/src/nspawn/nspawn-cgroup.c +@@ -55,7 +55,7 @@ int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) { + if (r < 0) + return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs); + +- if (unified_requested == CGROUP_UNIFIED_SYSTEMD) { ++ if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) { + _cleanup_free_ char *lfs = NULL; + /* Always propagate access rights from unified to legacy controller */ + diff --git a/SOURCES/0376-core-move-unit_status_emit_starting_stopping_reloadi.patch b/SOURCES/0376-core-move-unit_status_emit_starting_stopping_reloadi.patch new file mode 100644 index 0000000..2badda5 --- /dev/null +++ b/SOURCES/0376-core-move-unit_status_emit_starting_stopping_reloadi.patch @@ -0,0 +1,354 @@ +From 883dfbff6e3e9763d21f9d029a824c63e016cfd9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 13 Nov 2018 19:57:43 +0100 +Subject: [PATCH] core: move unit_status_emit_starting_stopping_reloading() and + related calls to job.c +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This call is only used by job.c and very specific to job handling. +Moreover the very similar logic of job_emit_status_message() is already +in job.c. + +Hence, let's clean this up, and move both sets of functions to job.c, +and rename them a bit so that they express precisely what they do: + +1. unit_status_emit_starting_stopping_reloading() → + job_emit_begin_status_message() +2. job_emit_status_message() → job_emit_done_status_message() + +The first call is after all what we call when we begin with the +execution of a job, and the second call what we call when we are done +wiht it. + +Just some moving and renaming, not other changes, and hence no change in +behaviour. + +(cherry picked from commit 33a3fdd9781329379f74e11a7a2707816aad8c61) + +Related: #1737283 +--- + src/core/job.c | 119 +++++++++++++++++++++++++++++++++++++++++------- + src/core/unit.c | 86 ---------------------------------- + src/core/unit.h | 1 - + 3 files changed, 103 insertions(+), 103 deletions(-) + +diff --git a/src/core/job.c b/src/core/job.c +index 769ed6d603..561ea14682 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -507,6 +507,93 @@ static void job_change_type(Job *j, JobType newtype) { + j->type = newtype; + } + ++_pure_ static const char* job_get_begin_status_message_format(Unit *u, JobType t) { ++ const char *format; ++ const UnitStatusMessageFormats *format_table; ++ ++ assert(u); ++ assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)); ++ ++ if (t != JOB_RELOAD) { ++ format_table = &UNIT_VTABLE(u)->status_message_formats; ++ if (format_table) { ++ format = format_table->starting_stopping[t == JOB_STOP]; ++ if (format) ++ return format; ++ } ++ } ++ ++ /* Return generic strings */ ++ if (t == JOB_START) ++ return "Starting %s."; ++ else if (t == JOB_STOP) ++ return "Stopping %s."; ++ else ++ return "Reloading %s."; ++} ++ ++static void job_print_begin_status_message(Unit *u, JobType t) { ++ const char *format; ++ ++ assert(u); ++ ++ /* Reload status messages have traditionally not been printed to console. */ ++ if (!IN_SET(t, JOB_START, JOB_STOP)) ++ return; ++ ++ format = job_get_begin_status_message_format(u, t); ++ ++ DISABLE_WARNING_FORMAT_NONLITERAL; ++ unit_status_printf(u, "", format); ++ REENABLE_WARNING; ++} ++ ++static void job_log_begin_status_message(Unit *u, JobType t) { ++ const char *format, *mid; ++ char buf[LINE_MAX]; ++ ++ assert(u); ++ ++ if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) ++ return; ++ ++ if (log_on_console()) ++ return; ++ ++ /* We log status messages for all units and all operations. */ ++ ++ format = job_get_begin_status_message_format(u, t); ++ ++ DISABLE_WARNING_FORMAT_NONLITERAL; ++ (void) snprintf(buf, sizeof buf, format, unit_description(u)); ++ REENABLE_WARNING; ++ ++ mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR : ++ t == JOB_STOP ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR : ++ "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR; ++ ++ /* Note that we deliberately use LOG_MESSAGE() instead of ++ * LOG_UNIT_MESSAGE() here, since this is supposed to mimic ++ * closely what is written to screen using the status output, ++ * which is supposed the highest level, friendliest output ++ * possible, which means we should avoid the low-level unit ++ * name. */ ++ log_struct(LOG_INFO, ++ LOG_MESSAGE("%s", buf), ++ LOG_UNIT_ID(u), ++ LOG_UNIT_INVOCATION_ID(u), ++ mid); ++} ++ ++static void job_emit_begin_status_message(Unit *u, JobType t) { ++ assert(u); ++ assert(t >= 0); ++ assert(t < _JOB_TYPE_MAX); ++ ++ job_log_begin_status_message(u, t); ++ job_print_begin_status_message(u, t); ++} ++ + static int job_perform_on_unit(Job **j) { + uint32_t id; + Manager *m; +@@ -552,7 +639,7 @@ static int job_perform_on_unit(Job **j) { + * actually did something. */ + *j = manager_get_job(m, id); + if (*j && r > 0) +- unit_status_emit_starting_stopping_reloading(u, t); ++ job_emit_begin_status_message(u, t); + + return r; + } +@@ -638,7 +725,7 @@ int job_run_and_invalidate(Job *j) { + return r; + } + +-_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { ++_pure_ static const char *job_get_done_status_message_format(Unit *u, JobType t, JobResult result) { + + static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = { + [JOB_DONE] = "Started %s.", +@@ -699,7 +786,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR + + static const struct { + const char *color, *word; +-} job_print_status_messages [_JOB_RESULT_MAX] = { ++} job_print_done_status_messages[_JOB_RESULT_MAX] = { + [JOB_DONE] = { ANSI_OK_COLOR, " OK " }, + [JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " }, + [JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" }, +@@ -711,7 +798,7 @@ static const struct { + [JOB_ONCE] = { ANSI_HIGHLIGHT_RED, " ONCE " }, + }; + +-static void job_print_status_message(Unit *u, JobType t, JobResult result) { ++static void job_print_done_status_message(Unit *u, JobType t, JobResult result) { + const char *format; + const char *status; + +@@ -723,19 +810,19 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { + if (t == JOB_RELOAD) + return; + +- if (!job_print_status_messages[result].word) ++ if (!job_print_done_status_messages[result].word) + return; + +- format = job_get_status_message_format(u, t, result); ++ format = job_get_done_status_message_format(u, t, result); + if (!format) + return; + + if (log_get_show_color()) +- status = strjoina(job_print_status_messages[result].color, +- job_print_status_messages[result].word, ++ status = strjoina(job_print_done_status_messages[result].color, ++ job_print_done_status_messages[result].word, + ANSI_NORMAL); + else +- status = job_print_status_messages[result].word; ++ status = job_print_done_status_messages[result].word; + + if (result != JOB_DONE) + manager_flip_auto_status(u->manager, true); +@@ -752,7 +839,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { + } + } + +-static void job_log_status_message(Unit *u, JobType t, JobResult result) { ++static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) { + const char *format, *mid; + char buf[LINE_MAX]; + static const int job_result_log_level[_JOB_RESULT_MAX] = { +@@ -775,10 +862,10 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { + + /* Skip printing if output goes to the console, and job_print_status_message() + will actually print something to the console. */ +- if (log_on_console() && job_print_status_messages[result].word) ++ if (log_on_console() && job_print_done_status_messages[result].word) + return; + +- format = job_get_status_message_format(u, t, result); ++ format = job_get_done_status_message_format(u, t, result); + if (!format) + return; + +@@ -827,15 +914,15 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { + mid); + } + +-static void job_emit_status_message(Unit *u, JobType t, JobResult result) { ++static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) { + assert(u); + + /* No message if the job did not actually do anything due to failed condition. */ + if (t == JOB_START && result == JOB_DONE && !u->condition_result) + return; + +- job_log_status_message(u, t, result); +- job_print_status_message(u, t, result); ++ job_log_done_status_message(u, job_id, t, result); ++ job_print_done_status_message(u, t, result); + } + + static void job_fail_dependencies(Unit *u, UnitDependency d) { +@@ -890,7 +977,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr + + /* If this job did nothing to respective unit we don't log the status message */ + if (!already) +- job_emit_status_message(u, t, result); ++ job_emit_done_status_message(u, j->id, t, result); + + /* Patch restart jobs so that they become normal start jobs */ + if (result == JOB_DONE && t == JOB_RESTART) { +diff --git a/src/core/unit.c b/src/core/unit.c +index 40f138d25c..f5e251123d 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -1624,92 +1624,6 @@ void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg + REENABLE_WARNING; + } + +-_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) { +- const char *format; +- const UnitStatusMessageFormats *format_table; +- +- assert(u); +- assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)); +- +- if (t != JOB_RELOAD) { +- format_table = &UNIT_VTABLE(u)->status_message_formats; +- if (format_table) { +- format = format_table->starting_stopping[t == JOB_STOP]; +- if (format) +- return format; +- } +- } +- +- /* Return generic strings */ +- if (t == JOB_START) +- return "Starting %s."; +- else if (t == JOB_STOP) +- return "Stopping %s."; +- else +- return "Reloading %s."; +-} +- +-static void unit_status_print_starting_stopping(Unit *u, JobType t) { +- const char *format; +- +- assert(u); +- +- /* Reload status messages have traditionally not been printed to console. */ +- if (!IN_SET(t, JOB_START, JOB_STOP)) +- return; +- +- format = unit_get_status_message_format(u, t); +- +- DISABLE_WARNING_FORMAT_NONLITERAL; +- unit_status_printf(u, "", format); +- REENABLE_WARNING; +-} +- +-static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { +- const char *format, *mid; +- char buf[LINE_MAX]; +- +- assert(u); +- +- if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) +- return; +- +- if (log_on_console()) +- return; +- +- /* We log status messages for all units and all operations. */ +- +- format = unit_get_status_message_format(u, t); +- +- DISABLE_WARNING_FORMAT_NONLITERAL; +- (void) snprintf(buf, sizeof buf, format, unit_description(u)); +- REENABLE_WARNING; +- +- mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR : +- t == JOB_STOP ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR : +- "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR; +- +- /* Note that we deliberately use LOG_MESSAGE() instead of +- * LOG_UNIT_MESSAGE() here, since this is supposed to mimic +- * closely what is written to screen using the status output, +- * which is supposed the highest level, friendliest output +- * possible, which means we should avoid the low-level unit +- * name. */ +- log_struct(LOG_INFO, +- LOG_MESSAGE("%s", buf), +- LOG_UNIT_ID(u), +- LOG_UNIT_INVOCATION_ID(u), +- mid); +-} +- +-void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { +- assert(u); +- assert(t >= 0); +- assert(t < _JOB_TYPE_MAX); +- +- unit_status_log_starting_stopping_reloading(u, t); +- unit_status_print_starting_stopping(u, t); +-} + + int unit_start_limit_test(Unit *u) { + assert(u); +diff --git a/src/core/unit.h b/src/core/unit.h +index 595ee88d43..4d9539790a 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -699,7 +699,6 @@ int unit_coldplug(Unit *u); + void unit_catchup(Unit *u); + + void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0); +-void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t); + + bool unit_need_daemon_reload(Unit *u); + diff --git a/SOURCES/0377-job-when-a-job-was-skipped-due-to-a-failed-condition.patch b/SOURCES/0377-job-when-a-job-was-skipped-due-to-a-failed-condition.patch new file mode 100644 index 0000000..c7f1a68 --- /dev/null +++ b/SOURCES/0377-job-when-a-job-was-skipped-due-to-a-failed-condition.patch @@ -0,0 +1,63 @@ +From 9c543783dbe560f4dafa4c2f276e03a4bce0389e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 14 Nov 2018 11:08:16 +0100 +Subject: [PATCH] job: when a job was skipped due to a failed condition, log + about it + +Previously we'd neither show console status output nor log output. Let's +fix that, and still log something. + +(cherry picked from commit 9a80f2f4533883d272e6a436512aa7e88cedc549) + +Related: #1737283 +--- + src/core/job.c | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/src/core/job.c b/src/core/job.c +index 561ea14682..b9eee91cf3 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -810,6 +810,10 @@ static void job_print_done_status_message(Unit *u, JobType t, JobResult result) + if (t == JOB_RELOAD) + return; + ++ /* No message if the job did not actually do anything due to failed condition. */ ++ if (t == JOB_START && result == JOB_DONE && !u->condition_result) ++ return; ++ + if (!job_print_done_status_messages[result].word) + return; + +@@ -865,6 +869,20 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job + if (log_on_console() && job_print_done_status_messages[result].word) + return; + ++ /* Show condition check message if the job did not actually do anything due to failed condition. */ ++ if (t == JOB_START && result == JOB_DONE && !u->condition_result) { ++ log_struct(LOG_INFO, ++ "MESSAGE=Condition check resulted in %s being skipped.", unit_description(u), ++ "JOB_ID=%" PRIu32, job_id, ++ "JOB_TYPE=%s", job_type_to_string(t), ++ "JOB_RESULT=%s", job_result_to_string(result), ++ LOG_UNIT_ID(u), ++ LOG_UNIT_INVOCATION_ID(u), ++ "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR); ++ ++ return; ++ } ++ + format = job_get_done_status_message_format(u, t, result); + if (!format) + return; +@@ -917,10 +935,6 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job + static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) { + assert(u); + +- /* No message if the job did not actually do anything due to failed condition. */ +- if (t == JOB_START && result == JOB_DONE && !u->condition_result) +- return; +- + job_log_done_status_message(u, job_id, t, result); + job_print_done_status_message(u, t, result); + } diff --git a/SOURCES/0378-core-split-out-all-logic-that-updates-a-Job-on-a-uni.patch b/SOURCES/0378-core-split-out-all-logic-that-updates-a-Job-on-a-uni.patch new file mode 100644 index 0000000..eb29a28 --- /dev/null +++ b/SOURCES/0378-core-split-out-all-logic-that-updates-a-Job-on-a-uni.patch @@ -0,0 +1,169 @@ +From 81c3f90d41c973a18e157c1106926711815adc0e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 10 Dec 2018 20:56:57 +0100 +Subject: [PATCH] core: split out all logic that updates a Job on a unit's + unit_notify() invocation + +Just some refactoring, no change in behaviour. + +(cherry picked from commit 16c74914d233ec93012d77e5f93cf90e42939669) + +Related: #1737283 +--- + src/core/unit.c | 136 +++++++++++++++++++++++++----------------------- + 1 file changed, 71 insertions(+), 65 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index f5e251123d..a4865c1da5 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -2225,6 +2225,73 @@ static void unit_update_on_console(Unit *u) { + manager_unref_console(u->manager); + } + ++static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) { ++ bool unexpected = false; ++ ++ assert(j); ++ ++ if (j->state == JOB_WAITING) ++ ++ /* So we reached a different state for this job. Let's see if we can run it now if it failed previously ++ * due to EAGAIN. */ ++ job_add_to_run_queue(j); ++ ++ /* Let's check whether the unit's new state constitutes a finished job, or maybe contradicts a running job and ++ * hence needs to invalidate jobs. */ ++ ++ switch (j->type) { ++ ++ case JOB_START: ++ case JOB_VERIFY_ACTIVE: ++ ++ if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) ++ job_finish_and_invalidate(j, JOB_DONE, true, false); ++ else if (j->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { ++ unexpected = true; ++ ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) ++ job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); ++ } ++ ++ break; ++ ++ case JOB_RELOAD: ++ case JOB_RELOAD_OR_START: ++ case JOB_TRY_RELOAD: ++ ++ if (j->state == JOB_RUNNING) { ++ if (ns == UNIT_ACTIVE) ++ job_finish_and_invalidate(j, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false); ++ else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) { ++ unexpected = true; ++ ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) ++ job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); ++ } ++ } ++ ++ break; ++ ++ case JOB_STOP: ++ case JOB_RESTART: ++ case JOB_TRY_RESTART: ++ ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) ++ job_finish_and_invalidate(j, JOB_DONE, true, false); ++ else if (j->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { ++ unexpected = true; ++ job_finish_and_invalidate(j, JOB_FAILED, true, false); ++ } ++ ++ break; ++ ++ default: ++ assert_not_reached("Job type unknown"); ++ } ++ ++ return unexpected; ++} ++ + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) { + bool unexpected; + Manager *m; +@@ -2265,71 +2332,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag + + unit_update_on_console(u); + +- if (u->job) { +- unexpected = false; +- +- if (u->job->state == JOB_WAITING) +- +- /* So we reached a different state for this +- * job. Let's see if we can run it now if it +- * failed previously due to EAGAIN. */ +- job_add_to_run_queue(u->job); +- +- /* Let's check whether this state change constitutes a +- * finished job, or maybe contradicts a running job and +- * hence needs to invalidate jobs. */ +- +- switch (u->job->type) { +- +- case JOB_START: +- case JOB_VERIFY_ACTIVE: +- +- if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) +- job_finish_and_invalidate(u->job, JOB_DONE, true, false); +- else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { +- unexpected = true; +- +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); +- } +- +- break; +- +- case JOB_RELOAD: +- case JOB_RELOAD_OR_START: +- case JOB_TRY_RELOAD: +- +- if (u->job->state == JOB_RUNNING) { +- if (ns == UNIT_ACTIVE) +- job_finish_and_invalidate(u->job, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false); +- else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) { +- unexpected = true; +- +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); +- } +- } +- +- break; +- +- case JOB_STOP: +- case JOB_RESTART: +- case JOB_TRY_RESTART: +- +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(u->job, JOB_DONE, true, false); +- else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { +- unexpected = true; +- job_finish_and_invalidate(u->job, JOB_FAILED, true, false); +- } +- +- break; +- +- default: +- assert_not_reached("Job type unknown"); +- } +- +- } else ++ /* Let's propagate state changes to the job */ ++ if (u->job) ++ unexpected = unit_process_job(u->job, ns, flags); ++ else + unexpected = true; + + if (!MANAGER_IS_RELOADING(m)) { diff --git a/SOURCES/0379-core-make-log-messages-about-units-entering-a-failed.patch b/SOURCES/0379-core-make-log-messages-about-units-entering-a-failed.patch new file mode 100644 index 0000000..ff88dd4 --- /dev/null +++ b/SOURCES/0379-core-make-log-messages-about-units-entering-a-failed.patch @@ -0,0 +1,200 @@ +From d5245b46716cf53ce4d95966ea99499cf7aa209a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 13 Nov 2018 21:25:22 +0100 +Subject: [PATCH] core: make log messages about units entering a 'failed' state + recognizable + +Let's make this recognizable, and carry result information in a +structure fashion. + +(cherry picked from commit 7c047d7443347c109daf67023a01c118b5f361eb) + +Related: #1737283 +--- + catalog/systemd.catalog.in | 7 +++++++ + src/core/automount.c | 2 +- + src/core/mount.c | 2 +- + src/core/path.c | 2 +- + src/core/scope.c | 2 +- + src/core/service.c | 2 +- + src/core/socket.c | 2 +- + src/core/swap.c | 2 +- + src/core/timer.c | 2 +- + src/core/unit.c | 12 ++++++++++++ + src/core/unit.h | 2 ++ + src/systemd/sd-messages.h | 4 ++++ + 12 files changed, 33 insertions(+), 8 deletions(-) + +diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in +index 49a45890f6..54a0f46921 100644 +--- a/catalog/systemd.catalog.in ++++ b/catalog/systemd.catalog.in +@@ -344,6 +344,13 @@ Support: %SUPPORT_URL% + + The unit @UNIT@ completed and consumed the indicated resources. + ++-- d9b373ed55a64feb8242e02dbe79a49c ++Subject: Unit failed ++Defined-By: systemd ++Support: %SUPPORT_URL% ++ ++The unit @UNIT@ has entered the 'failed' state with result '@UNIT_RESULT@'. ++ + -- 50876a9db00f4c40bde1a2ad381c3a1b + Subject: The system is configured in a way that might cause problems + Defined-By: systemd +diff --git a/src/core/automount.c b/src/core/automount.c +index 1b96a52c00..c78562c549 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -315,7 +315,7 @@ static void automount_enter_dead(Automount *a, AutomountResult f) { + a->result = f; + + if (a->result != AUTOMOUNT_SUCCESS) +- log_unit_warning(UNIT(a), "Failed with result '%s'.", automount_result_to_string(a->result)); ++ unit_log_failure(UNIT(a), automount_result_to_string(a->result)); + + automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); + } +diff --git a/src/core/mount.c b/src/core/mount.c +index 5878814b1b..3cd0e479e9 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -797,7 +797,7 @@ static void mount_enter_dead(Mount *m, MountResult f) { + m->result = f; + + if (m->result != MOUNT_SUCCESS) +- log_unit_warning(UNIT(m), "Failed with result '%s'.", mount_result_to_string(m->result)); ++ unit_log_failure(UNIT(m), mount_result_to_string(m->result)); + + mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); + +diff --git a/src/core/path.c b/src/core/path.c +index 5ef178a46b..f8b69e7804 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -449,7 +449,7 @@ static void path_enter_dead(Path *p, PathResult f) { + p->result = f; + + if (p->result != PATH_SUCCESS) +- log_unit_warning(UNIT(p), "Failed with result '%s'.", path_result_to_string(p->result)); ++ unit_log_failure(UNIT(p), path_result_to_string(p->result)); + + path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); + } +diff --git a/src/core/scope.c b/src/core/scope.c +index 751556fecf..79ecfd992f 100644 +--- a/src/core/scope.c ++++ b/src/core/scope.c +@@ -240,7 +240,7 @@ static void scope_enter_dead(Scope *s, ScopeResult f) { + s->result = f; + + if (s->result != SCOPE_SUCCESS) +- log_unit_warning(UNIT(s), "Failed with result '%s'.", scope_result_to_string(s->result)); ++ unit_log_failure(UNIT(s), scope_result_to_string(s->result)); + + scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); + } +diff --git a/src/core/service.c b/src/core/service.c +index 5035dcacac..efceb0614c 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -1680,7 +1680,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + s->result = f; + + if (s->result != SERVICE_SUCCESS) +- log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result)); ++ unit_log_failure(UNIT(s), service_result_to_string(s->result)); + + if (allow_restart && service_shall_restart(s)) + s->will_auto_restart = true; +diff --git a/src/core/socket.c b/src/core/socket.c +index b034549634..160f11765c 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -1991,7 +1991,7 @@ static void socket_enter_dead(Socket *s, SocketResult f) { + s->result = f; + + if (s->result != SOCKET_SUCCESS) +- log_unit_warning(UNIT(s), "Failed with result '%s'.", socket_result_to_string(s->result)); ++ unit_log_failure(UNIT(s), socket_result_to_string(s->result)); + + socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); + +diff --git a/src/core/swap.c b/src/core/swap.c +index 66a62d8a37..b5926d8644 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -657,7 +657,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) { + s->result = f; + + if (s->result != SWAP_SUCCESS) +- log_unit_warning(UNIT(s), "Failed with result '%s'.", swap_result_to_string(s->result)); ++ unit_log_failure(UNIT(s), swap_result_to_string(s->result)); + + swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); + +diff --git a/src/core/timer.c b/src/core/timer.c +index db202971d3..6ac310cbe0 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -288,7 +288,7 @@ static void timer_enter_dead(Timer *t, TimerResult f) { + t->result = f; + + if (t->result != TIMER_SUCCESS) +- log_unit_warning(UNIT(t), "Failed with result '%s'.", timer_result_to_string(t->result)); ++ unit_log_failure(UNIT(t), timer_result_to_string(t->result)); + + timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); + } +diff --git a/src/core/unit.c b/src/core/unit.c +index a4865c1da5..f55bddc00f 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -5462,6 +5462,18 @@ int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) { + return 0; + } + ++void unit_log_failure(Unit *u, const char *result) { ++ assert(u); ++ assert(result); ++ ++ log_struct(LOG_WARNING, ++ "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILURE_RESULT_STR, ++ LOG_UNIT_ID(u), ++ LOG_UNIT_INVOCATION_ID(u), ++ LOG_UNIT_MESSAGE(u, "Failed with result '%s'.", result), ++ "UNIT_RESULT=%s", result); ++} ++ + 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 4d9539790a..9d226fb3e0 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -804,6 +804,8 @@ const char *unit_label_path(Unit *u); + + int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error); + ++void unit_log_failure(Unit *u, const char *result); ++ + /* Macros which append UNIT= or USER_UNIT= to the message */ + + #define log_unit_full(unit, level, error, ...) \ +diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h +index 2adfe16062..846b28fc2b 100644 +--- a/src/systemd/sd-messages.h ++++ b/src/systemd/sd-messages.h +@@ -106,6 +106,10 @@ _SD_BEGIN_DECLARATIONS; + #define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) + #define SD_MESSAGE_UNIT_RESOURCES_STR SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) + ++#define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) ++#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR \ ++ SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) ++ + #define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + #define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + diff --git a/SOURCES/0380-core-log-a-recognizable-message-when-a-unit-succeeds.patch b/SOURCES/0380-core-log-a-recognizable-message-when-a-unit-succeeds.patch new file mode 100644 index 0000000..a526810 --- /dev/null +++ b/SOURCES/0380-core-log-a-recognizable-message-when-a-unit-succeeds.patch @@ -0,0 +1,210 @@ +From 40c2b0a20ff133f2050642dc7230424ddcb2987b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 13 Nov 2018 23:28:09 +0100 +Subject: [PATCH] core: log a recognizable message when a unit succeeds, too + +We already are doing it on failure, let's do it on success, too. + +Fixes: #10265 +(cherry picked from commit 523ee2d41471bfb738f52d59de9b469301842644) + +Related: #1737283 +--- + catalog/systemd.catalog.in | 7 +++++++ + src/core/automount.c | 4 +++- + src/core/mount.c | 4 +++- + src/core/path.c | 4 +++- + src/core/scope.c | 4 +++- + src/core/service.c | 4 +++- + src/core/socket.c | 4 +++- + src/core/swap.c | 4 +++- + src/core/timer.c | 4 +++- + src/core/unit.c | 10 ++++++++++ + src/core/unit.h | 1 + + src/systemd/sd-messages.h | 2 ++ + 12 files changed, 44 insertions(+), 8 deletions(-) + +diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in +index 54a0f46921..2492ad2028 100644 +--- a/catalog/systemd.catalog.in ++++ b/catalog/systemd.catalog.in +@@ -344,6 +344,13 @@ Support: %SUPPORT_URL% + + The unit @UNIT@ completed and consumed the indicated resources. + ++-- 7ad2d189f7e94e70a38c781354912448 ++Subject: Unit succeeded ++Defined-By: systemd ++Support: %SUPPORT_URL% ++ ++The unit @UNIT@ has successfully entered the 'dead' state. ++ + -- d9b373ed55a64feb8242e02dbe79a49c + Subject: Unit failed + Defined-By: systemd +diff --git a/src/core/automount.c b/src/core/automount.c +index c78562c549..b1a155d8d4 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -314,7 +314,9 @@ static void automount_enter_dead(Automount *a, AutomountResult f) { + if (a->result == AUTOMOUNT_SUCCESS) + a->result = f; + +- if (a->result != AUTOMOUNT_SUCCESS) ++ if (a->result == AUTOMOUNT_SUCCESS) ++ unit_log_success(UNIT(a)); ++ else + unit_log_failure(UNIT(a), automount_result_to_string(a->result)); + + automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); +diff --git a/src/core/mount.c b/src/core/mount.c +index 3cd0e479e9..30aaf5ae55 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -796,7 +796,9 @@ static void mount_enter_dead(Mount *m, MountResult f) { + if (m->result == MOUNT_SUCCESS) + m->result = f; + +- if (m->result != MOUNT_SUCCESS) ++ if (m->result == MOUNT_SUCCESS) ++ unit_log_success(UNIT(m)); ++ else + unit_log_failure(UNIT(m), mount_result_to_string(m->result)); + + mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); +diff --git a/src/core/path.c b/src/core/path.c +index f8b69e7804..dda4a3036b 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -448,7 +448,9 @@ static void path_enter_dead(Path *p, PathResult f) { + if (p->result == PATH_SUCCESS) + p->result = f; + +- if (p->result != PATH_SUCCESS) ++ if (p->result == PATH_SUCCESS) ++ unit_log_success(UNIT(p)); ++ else + unit_log_failure(UNIT(p), path_result_to_string(p->result)); + + path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); +diff --git a/src/core/scope.c b/src/core/scope.c +index 79ecfd992f..a1a5363244 100644 +--- a/src/core/scope.c ++++ b/src/core/scope.c +@@ -239,7 +239,9 @@ static void scope_enter_dead(Scope *s, ScopeResult f) { + if (s->result == SCOPE_SUCCESS) + s->result = f; + +- if (s->result != SCOPE_SUCCESS) ++ if (s->result == SCOPE_SUCCESS) ++ unit_log_success(UNIT(s)); ++ else + unit_log_failure(UNIT(s), scope_result_to_string(s->result)); + + scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); +diff --git a/src/core/service.c b/src/core/service.c +index efceb0614c..2c31e70ef6 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -1679,7 +1679,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + if (s->result == SERVICE_SUCCESS) + s->result = f; + +- if (s->result != SERVICE_SUCCESS) ++ if (s->result == SERVICE_SUCCESS) ++ unit_log_success(UNIT(s)); ++ else + unit_log_failure(UNIT(s), service_result_to_string(s->result)); + + if (allow_restart && service_shall_restart(s)) +diff --git a/src/core/socket.c b/src/core/socket.c +index 160f11765c..7c6d3dfad1 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -1990,7 +1990,9 @@ static void socket_enter_dead(Socket *s, SocketResult f) { + if (s->result == SOCKET_SUCCESS) + s->result = f; + +- if (s->result != SOCKET_SUCCESS) ++ if (s->result == SOCKET_SUCCESS) ++ unit_log_success(UNIT(s)); ++ else + unit_log_failure(UNIT(s), socket_result_to_string(s->result)); + + socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); +diff --git a/src/core/swap.c b/src/core/swap.c +index b5926d8644..a8f127f660 100644 +--- a/src/core/swap.c ++++ b/src/core/swap.c +@@ -656,7 +656,9 @@ static void swap_enter_dead(Swap *s, SwapResult f) { + if (s->result == SWAP_SUCCESS) + s->result = f; + +- if (s->result != SWAP_SUCCESS) ++ if (s->result == SWAP_SUCCESS) ++ unit_log_success(UNIT(s)); ++ else + unit_log_failure(UNIT(s), swap_result_to_string(s->result)); + + swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); +diff --git a/src/core/timer.c b/src/core/timer.c +index 6ac310cbe0..2876d54a59 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -287,7 +287,9 @@ static void timer_enter_dead(Timer *t, TimerResult f) { + if (t->result == TIMER_SUCCESS) + t->result = f; + +- if (t->result != TIMER_SUCCESS) ++ if (t->result == TIMER_SUCCESS) ++ unit_log_success(UNIT(t)); ++ else + unit_log_failure(UNIT(t), timer_result_to_string(t->result)); + + timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); +diff --git a/src/core/unit.c b/src/core/unit.c +index f55bddc00f..ccb0106719 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -5462,6 +5462,16 @@ int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) { + return 0; + } + ++void unit_log_success(Unit *u) { ++ assert(u); ++ ++ log_struct(LOG_INFO, ++ "MESSAGE_ID=" SD_MESSAGE_UNIT_SUCCESS_STR, ++ LOG_UNIT_ID(u), ++ LOG_UNIT_INVOCATION_ID(u), ++ LOG_UNIT_MESSAGE(u, "Succeeded.")); ++} ++ + void unit_log_failure(Unit *u, const char *result) { + assert(u); + assert(result); +diff --git a/src/core/unit.h b/src/core/unit.h +index 9d226fb3e0..4ae1b38624 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -804,6 +804,7 @@ const char *unit_label_path(Unit *u); + + int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error); + ++void unit_log_success(Unit *u); + void unit_log_failure(Unit *u, const char *result); + + /* Macros which append UNIT= or USER_UNIT= to the message */ +diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h +index 846b28fc2b..e7ef81b597 100644 +--- a/src/systemd/sd-messages.h ++++ b/src/systemd/sd-messages.h +@@ -106,6 +106,8 @@ _SD_BEGIN_DECLARATIONS; + #define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) + #define SD_MESSAGE_UNIT_RESOURCES_STR SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0) + ++#define SD_MESSAGE_UNIT_SUCCESS SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48) ++#define SD_MESSAGE_UNIT_SUCCESS_STR SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48) + #define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) + #define SD_MESSAGE_UNIT_FAILURE_RESULT_STR \ + SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) diff --git a/SOURCES/0381-tests-always-use-the-right-vtable-wrapper-calls.patch b/SOURCES/0381-tests-always-use-the-right-vtable-wrapper-calls.patch new file mode 100644 index 0000000..d10b974 --- /dev/null +++ b/SOURCES/0381-tests-always-use-the-right-vtable-wrapper-calls.patch @@ -0,0 +1,113 @@ +From 9a6fa8659e7c6b18c95754e6fa9417f03b6341df Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 19 Nov 2018 14:48:28 +0100 +Subject: [PATCH] tests: always use the right vtable wrapper calls + +Prompted by https://github.com/systemd/systemd/pull/10836#discussion_r234598868 + +(cherry picked from commit bd7989a3d90e5d97e09f1eef33d09b2469a79f4d) + +Related: #1737283 +--- + src/test/test-execute.c | 2 +- + src/test/test-path.c | 18 +++++++++--------- + 2 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index 0f8dc883b1..d88de52d2a 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -129,7 +129,7 @@ static void test(Manager *m, const char *unit_name, int status_expected, int cod + assert_se(unit_name); + + assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + check(m, unit, status_expected, code_expected); + } + +diff --git a/src/test/test-path.c b/src/test/test-path.c +index 209eb2e366..3a1469ae02 100644 +--- a/src/test/test-path.c ++++ b/src/test/test-path.c +@@ -106,7 +106,7 @@ static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, con + } + } + +- assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0); ++ assert_se(unit_stop(unit) >= 0); + (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); + } + +@@ -117,7 +117,7 @@ static void test_path_exists(Manager *m) { + assert_se(m); + + assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + assert_se(touch(test_path) >= 0); + +@@ -130,7 +130,7 @@ static void test_path_existsglob(Manager *m) { + + assert_se(m); + assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + assert_se(touch(test_path) >= 0); + +@@ -147,7 +147,7 @@ static void test_path_changed(Manager *m) { + assert_se(touch(test_path) >= 0); + + assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + f = fopen(test_path, "w"); + assert_se(f); +@@ -166,7 +166,7 @@ static void test_path_modified(Manager *m) { + assert_se(touch(test_path) >= 0); + + assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + f = fopen(test_path, "w"); + assert_se(f); +@@ -182,7 +182,7 @@ static void test_path_unit(Manager *m) { + assert_se(m); + + assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + assert_se(touch(test_path) >= 0); + +@@ -198,7 +198,7 @@ static void test_path_directorynotempty(Manager *m) { + assert_se(access(test_path, F_OK) < 0); + + assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + /* MakeDirectory default to no */ + assert_se(access(test_path, F_OK) < 0); +@@ -219,7 +219,7 @@ static void test_path_makedirectory_directorymode(Manager *m) { + assert_se(access(test_path, F_OK) < 0); + + assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0); +- assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); ++ assert_se(unit_start(unit) >= 0); + + /* Check if the directory has been created */ + assert_se(access(test_path, F_OK) >= 0); +@@ -230,7 +230,7 @@ static void test_path_makedirectory_directorymode(Manager *m) { + assert_se((s.st_mode & S_IRWXG) == 0040); + assert_se((s.st_mode & S_IRWXO) == 0004); + +- assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0); ++ assert_se(unit_stop(unit) >= 0); + (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); + } + diff --git a/SOURCES/0382-test-execute-allow-filtering-test-cases-by-pattern.patch b/SOURCES/0382-test-execute-allow-filtering-test-cases-by-pattern.patch new file mode 100644 index 0000000..da50009 --- /dev/null +++ b/SOURCES/0382-test-execute-allow-filtering-test-cases-by-pattern.patch @@ -0,0 +1,153 @@ +From 9beae637a919326ddc072c4dd53cb66e80273c8f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 15 Mar 2019 13:42:55 +0100 +Subject: [PATCH] test-execute: allow filtering test cases by pattern + +When debugging failure in one of the cases, it's annoying to have to wade +through the output from all the other cases. Let's allow picking select +cases. + +(cherry picked from commit 9efb96315ae502dabeb94ab35816ea8955563b7a) + +Related: #1737283 +--- + src/test/test-execute.c | 104 ++++++++++++++++++++++------------------ + 1 file changed, 58 insertions(+), 46 deletions(-) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index d88de52d2a..d077674efc 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -659,8 +659,15 @@ static void test_exec_standardoutput_append(Manager *m) { + test(m, "exec-standardoutput-append.service", 0, CLD_EXITED); + } + +-static int run_tests(UnitFileScope scope, const test_function_t *tests) { +- const test_function_t *test = NULL; ++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; + +@@ -674,55 +681,60 @@ 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; +- 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, ++ _cleanup_free_ char *test_execute_path = 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_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; + +@@ -759,9 +771,9 @@ 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; + +- return run_tests(UNIT_FILE_SYSTEM, system_tests); ++ return run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); + } diff --git a/SOURCES/0383-test-execute-provide-custom-failure-message.patch b/SOURCES/0383-test-execute-provide-custom-failure-message.patch new file mode 100644 index 0000000..3299943 --- /dev/null +++ b/SOURCES/0383-test-execute-provide-custom-failure-message.patch @@ -0,0 +1,568 @@ +From ad8f0b480799aa7e312dacbcb0c01a3a9e6aa6db Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 26 Mar 2019 11:38:55 +0100 +Subject: [PATCH] test-execute: provide custom failure message + +test_exec_ambientcapabilities: exec-ambientcapabilities-nobody.service: exit status 0, expected 1 + +Sometimes we get just the last line, for example from the failure summary, +so make it as useful as possible. + +(cherry picked from commit 6aed6a11577b108b9a39f26aeae5e45d98f20c90) + +Related: #1737283 +--- + src/test/test-execute.c | 236 +++++++++++++++++++++------------------- + 1 file changed, 123 insertions(+), 113 deletions(-) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index d077674efc..e42d0d30a8 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -30,7 +30,7 @@ + + 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; +@@ -56,8 +56,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) { +@@ -123,21 +133,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", 0, CLD_EXITED); ++ test(__func__, m, "exec-bindpaths.service", 0, CLD_EXITED); + + (void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL); + (void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL); +@@ -154,8 +164,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, c.allocated, c.set) || + !CPU_ISSET_S(2, c.allocated, c.set)) { +@@ -163,52 +173,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", 0, CLD_EXITED); +- test(m, "exec-privatetmp-no.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-privatetmp-yes.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-privatetmp-no.service", 0, CLD_EXITED); + + unlink("/tmp/test-exec_privatetmp"); + } +@@ -225,9 +235,9 @@ static void test_exec_privatedevices(Manager *m) { + return; + } + +- test(m, "exec-privatedevices-yes.service", 0, CLD_EXITED); +- test(m, "exec-privatedevices-no.service", 0, CLD_EXITED); +- test(m, "exec-privatedevices-disabled-by-prefix.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-privatedevices-yes.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-privatedevices-no.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-privatedevices-disabled-by-prefix.service", 0, CLD_EXITED); + + /* We use capsh to test if the capabilities are + * properly set, so be sure that it exists */ +@@ -237,10 +247,10 @@ 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_protectkernelmodules(Manager *m) { +@@ -261,23 +271,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", 0, 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", 0, CLD_EXITED); + } + + static void test_exec_readonlypaths(Manager *m) { + +- test(m, "exec-readonlypaths-simple.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-readonlypaths-simple.service", 0, CLD_EXITED); + + if (path_is_read_only_fs("/var") > 0) { + log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__); + return; + } + +- test(m, "exec-readonlypaths.service", 0, CLD_EXITED); +- test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED); +- test(m, "exec-readonlypaths-with-bindpaths.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-readonlypaths.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-readonlypaths-with-bindpaths.service", 0, CLD_EXITED); + } + + static void test_exec_readwritepaths(Manager *m) { +@@ -287,7 +297,7 @@ static void test_exec_readwritepaths(Manager *m) { + return; + } + +- test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED); + } + + static void test_exec_inaccessiblepaths(Manager *m) { +@@ -297,22 +307,22 @@ static void test_exec_inaccessiblepaths(Manager *m) { + return; + } + +- test(m, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED); + + if (path_is_read_only_fs("/") > 0) { + log_notice("Root directory is readonly, skipping remaining tests in %s", __func__); + return; + } + +- test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED); + } + + static void test_exec_temporaryfilesystem(Manager *m) { + +- test(m, "exec-temporaryfilesystem-options.service", 0, CLD_EXITED); +- test(m, "exec-temporaryfilesystem-ro.service", 0, CLD_EXITED); +- test(m, "exec-temporaryfilesystem-rw.service", 0, CLD_EXITED); +- test(m, "exec-temporaryfilesystem-usr.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-temporaryfilesystem-options.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-temporaryfilesystem-ro.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-temporaryfilesystem-rw.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-temporaryfilesystem-usr.service", 0, CLD_EXITED); + } + + static void test_exec_systemcallfilter(Manager *m) { +@@ -324,10 +334,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) { +@@ -335,8 +345,8 @@ 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(__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); + #endif + } + +@@ -355,8 +365,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 + } + +@@ -367,13 +377,13 @@ static void test_exec_restrictnamespaces(Manager *m) { + return; + } + +- test(m, "exec-restrictnamespaces-no.service", 0, CLD_EXITED); +- test(m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED); +- test(m, "exec-restrictnamespaces-mnt.service", 0, CLD_EXITED); +- test(m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED); +- test(m, "exec-restrictnamespaces-merge-and.service", 0, CLD_EXITED); +- test(m, "exec-restrictnamespaces-merge-or.service", 0, CLD_EXITED); +- test(m, "exec-restrictnamespaces-merge-all.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-no.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-mnt.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-merge-and.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-merge-or.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-restrictnamespaces-merge-all.service", 0, CLD_EXITED); + #endif + } + +@@ -384,7 +394,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__); +@@ -396,12 +406,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__); +@@ -413,11 +423,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__); +@@ -429,31 +439,31 @@ 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", 0, CLD_EXITED); +- test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", 0, CLD_EXITED); +- test(m, "exec-dynamicuser-supplementarygroups.service", 0, CLD_EXITED); +- test(m, "exec-dynamicuser-statedir.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-dynamicuser-fixeduser.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-dynamicuser-supplementarygroups.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-dynamicuser-statedir.service", 0, CLD_EXITED); + + (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); + (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL); + (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", 0, CLD_EXITED); ++ test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-dynamicuser-statedir-migrate-step2.service", 0, CLD_EXITED); + + (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); + (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL); +@@ -462,9 +472,9 @@ 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.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) { +@@ -484,7 +494,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"); + } +@@ -506,26 +516,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__); +@@ -537,7 +547,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) { +@@ -556,14 +566,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) { +@@ -585,8 +595,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__); +@@ -598,8 +608,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) { +@@ -611,52 +621,52 @@ static void test_exec_privatenetwork(Manager *m) { + return; + } + +- test(m, "exec-privatenetwork-yes.service", 0, CLD_EXITED); ++ test(__func__, m, "exec-privatenetwork-yes.service", 0, 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); + } + + typedef struct test_entry { diff --git a/SOURCES/0384-core-ExecCondition-for-services.patch b/SOURCES/0384-core-ExecCondition-for-services.patch new file mode 100644 index 0000000..1d1e10f --- /dev/null +++ b/SOURCES/0384-core-ExecCondition-for-services.patch @@ -0,0 +1,738 @@ +From ab9c835796a27f0fbaee75a90f0311ec456941d8 Mon Sep 17 00:00:00 2001 +From: Anita Zhang +Date: Fri, 28 Jun 2019 17:02:30 -0700 +Subject: [PATCH] core: ExecCondition= for services + +Closes #10596 + +(cherry picked from commit 31cd5f63ce86a0784c4ef869c4d323a11ff14adc) + +Resolves: #1737283 +--- + TODO | 2 - + catalog/systemd.catalog.in | 7 ++ + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.service.xml | 20 ++++ + src/basic/unit-def.c | 1 + + src/basic/unit-def.h | 1 + + src/core/dbus-service.c | 1 + + src/core/job.c | 3 +- + src/core/job.h | 2 +- + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/service.c | 93 ++++++++++++++++--- + src/core/service.h | 2 + + src/core/unit.c | 25 ++++- + src/core/unit.h | 4 + + src/shared/bus-unit-util.c | 2 +- + src/systemd/sd-messages.h | 2 + + src/test/test-execute.c | 50 +++++++++- + test/fuzz/fuzz-unit-file/directives.service | 1 + + test/meson.build | 2 + + .../exec-condition-failed.service | 11 +++ + test/test-execute/exec-condition-skip.service | 15 +++ + 21 files changed, 222 insertions(+), 24 deletions(-) + create mode 100644 test/test-execute/exec-condition-failed.service + create mode 100644 test/test-execute/exec-condition-skip.service + +diff --git a/TODO b/TODO +index ff1008accf..8f78000089 100644 +--- a/TODO ++++ b/TODO +@@ -626,8 +626,6 @@ Features: + + * merge unit_kill_common() and unit_kill_context() + +-* introduce ExecCondition= in services +- + * EFI: + - honor language efi variables for default language selection (if there are any?) + - honor timezone efi variables for default timezone selection (if there are any?) +diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in +index 2492ad2028..dc44414f9d 100644 +--- a/catalog/systemd.catalog.in ++++ b/catalog/systemd.catalog.in +@@ -358,6 +358,13 @@ Support: %SUPPORT_URL% + + The unit @UNIT@ has entered the 'failed' state with result '@UNIT_RESULT@'. + ++-- 0e4284a0caca4bfc81c0bb6786972673 ++Subject: Unit skipped ++Defined-By: systemd ++Support: %SUPPORT_URL% ++ ++The unit @UNIT@ was skipped and has entered the 'dead' state with result '@UNIT_RESULT@'. ++ + -- 50876a9db00f4c40bde1a2ad381c3a1b + Subject: The system is configured in a way that might cause problems + Defined-By: systemd +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 0b2ad66dcb..23fe84e4d1 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -267,6 +267,7 @@ Most service unit settings are available for transient units. + + ``` + ✓ PIDFile= ++✓ ExecCondition= + ✓ ExecStartPre= + ✓ ExecStart= + ✓ ExecStartPost= +diff --git a/man/systemd.service.xml b/man/systemd.service.xml +index 315b80e704..54586d1948 100644 +--- a/man/systemd.service.xml ++++ b/man/systemd.service.xml +@@ -414,6 +414,26 @@ + + + ++ ++ ExecCondition= ++ Optional commands that are executed before the command(s) in ExecStartPre=. ++ Syntax is the same as for ExecStart=, except that multiple command lines are allowed and the ++ commands are executed one after the other, serially. ++ ++ The behavior is like an ExecStartPre= and condition check hybrid: when an ++ ExecCondition= command exits with exit code 1 through 254 (inclusive), the remaining ++ commands are skipped and the unit is not marked as failed. However, if an ++ ExecCondition= command exits with 255 or abnormally (e.g. timeout, killed by a ++ signal, etc.), the unit will be considered failed (and remaining commands will be skipped). Exit code of 0 or ++ those matching SuccessExitStatus= will continue execution to the next command(s). ++ ++ The same recommendations about not running long-running processes in ExecStartPre= ++ also applies to ExecCondition=. ExecCondition= will also run the commands ++ in ExecStopPost=, as part of stopping the service, in the case of any non-zero or abnormal ++ exits, like the ones described above. ++ ++ ++ + + ExecReload= + Commands to execute to trigger a configuration +diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c +index ac6a9b37e8..46593f6e65 100644 +--- a/src/basic/unit-def.c ++++ b/src/basic/unit-def.c +@@ -162,6 +162,7 @@ DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState); + + static const char* const service_state_table[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = "dead", ++ [SERVICE_CONDITION] = "condition", + [SERVICE_START_PRE] = "start-pre", + [SERVICE_START] = "start", + [SERVICE_START_POST] = "start-post", +diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h +index d7e2d74669..db397a31ed 100644 +--- a/src/basic/unit-def.h ++++ b/src/basic/unit-def.h +@@ -101,6 +101,7 @@ typedef enum ScopeState { + + typedef enum ServiceState { + SERVICE_DEAD, ++ SERVICE_CONDITION, + SERVICE_START_PRE, + SERVICE_START, + SERVICE_START_POST, +diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c +index 1b4c98c7d2..5f768a77c8 100644 +--- a/src/core/dbus-service.c ++++ b/src/core/dbus-service.c +@@ -127,6 +127,7 @@ const sd_bus_vtable bus_service_vtable[] = { + SD_BUS_PROPERTY("NRestarts", "u", bus_property_get_unsigned, offsetof(Service, n_restarts), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + + BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), ++ BUS_EXEC_COMMAND_LIST_VTABLE("ExecCondition", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), +diff --git a/src/core/job.c b/src/core/job.c +index b9eee91cf3..870ec0a387 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -870,7 +870,8 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job + return; + + /* Show condition check message if the job did not actually do anything due to failed condition. */ +- if (t == JOB_START && result == JOB_DONE && !u->condition_result) { ++ if ((t == JOB_START && result == JOB_DONE && !u->condition_result) || ++ (t == JOB_START && result == JOB_SKIPPED)) { + log_struct(LOG_INFO, + "MESSAGE=Condition check resulted in %s being skipped.", unit_description(u), + "JOB_ID=%" PRIu32, job_id, +diff --git a/src/core/job.h b/src/core/job.h +index 2f5f3f3989..189fea20ca 100644 +--- a/src/core/job.h ++++ b/src/core/job.h +@@ -85,7 +85,7 @@ enum JobResult { + JOB_TIMEOUT, /* Job timeout elapsed */ + JOB_FAILED, /* Job failed */ + JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */ +- JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE */ ++ JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE or skip due to ExecCondition= */ + JOB_INVALID, /* JOB_RELOAD of inactive unit */ + JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */ + JOB_UNSUPPORTED, /* Couldn't start a unit, because the unit type is not supported on the system */ +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 161c5a2c82..8883818ff2 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -291,6 +291,7 @@ Unit.AssertNull, config_parse_unit_condition_null, 0, + Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode) + m4_dnl + Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) ++Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command) + Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) + Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command) + Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command) +diff --git a/src/core/service.c b/src/core/service.c +index 2c31e70ef6..92be4280f6 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -41,6 +41,7 @@ + + static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = UNIT_INACTIVE, ++ [SERVICE_CONDITION] = UNIT_ACTIVATING, + [SERVICE_START_PRE] = UNIT_ACTIVATING, + [SERVICE_START] = UNIT_ACTIVATING, + [SERVICE_START_POST] = UNIT_ACTIVATING, +@@ -62,6 +63,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { + * consider idle jobs active as soon as we start working on them */ + static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = { + [SERVICE_DEAD] = UNIT_INACTIVE, ++ [SERVICE_CONDITION] = UNIT_ACTIVE, + [SERVICE_START_PRE] = UNIT_ACTIVE, + [SERVICE_START] = UNIT_ACTIVE, + [SERVICE_START_POST] = UNIT_ACTIVE, +@@ -1024,7 +1026,7 @@ static void service_set_state(Service *s, ServiceState state) { + service_unwatch_pid_file(s); + + if (!IN_SET(state, +- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, ++ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RUNNING, + SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, +@@ -1042,7 +1044,7 @@ static void service_set_state(Service *s, ServiceState state) { + } + + if (!IN_SET(state, +- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, ++ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { +@@ -1057,7 +1059,7 @@ static void service_set_state(Service *s, ServiceState state) { + } + + if (!IN_SET(state, +- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, ++ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RUNNING, SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && +@@ -1080,7 +1082,8 @@ static void service_set_state(Service *s, ServiceState state) { + + unit_notify(UNIT(s), table[old_state], table[state], + (s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE) | +- (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0)); ++ (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0) | ++ (s->result == SERVICE_SKIP_CONDITION ? UNIT_NOTIFY_SKIP_CONDITION : 0)); + } + + static usec_t service_coldplug_timeout(Service *s) { +@@ -1088,6 +1091,7 @@ static usec_t service_coldplug_timeout(Service *s) { + + switch (s->deserialized_state) { + ++ case SERVICE_CONDITION: + case SERVICE_START_PRE: + case SERVICE_START: + case SERVICE_START_POST: +@@ -1143,7 +1147,7 @@ static int service_coldplug(Unit *u) { + if (s->control_pid > 0 && + pid_is_unwaited(s->control_pid) && + IN_SET(s->deserialized_state, +- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, ++ SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, + SERVICE_RELOAD, + SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) { +@@ -1667,6 +1671,7 @@ static bool service_will_restart(Unit *u) { + } + + static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { ++ ServiceState end_state; + int r; + + assert(s); +@@ -1679,10 +1684,16 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + if (s->result == SERVICE_SUCCESS) + s->result = f; + +- if (s->result == SERVICE_SUCCESS) ++ if (s->result == SERVICE_SUCCESS) { + unit_log_success(UNIT(s)); +- else ++ end_state = SERVICE_DEAD; ++ } else if (s->result == SERVICE_SKIP_CONDITION) { ++ unit_log_skip(UNIT(s), service_result_to_string(s->result)); ++ end_state = SERVICE_DEAD; ++ } else { + unit_log_failure(UNIT(s), service_result_to_string(s->result)); ++ end_state = SERVICE_FAILED; ++ } + + if (allow_restart && service_shall_restart(s)) + s->will_auto_restart = true; +@@ -1691,7 +1702,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + * SERVICE_FAILED/SERVICE_DEAD before entering into SERVICE_AUTO_RESTART. */ + s->n_keep_fd_store ++; + +- service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); ++ service_set_state(s, end_state); + + if (s->will_auto_restart) { + s->will_auto_restart = false; +@@ -2110,6 +2121,40 @@ fail: + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); + } + ++static void service_enter_condition(Service *s) { ++ int r; ++ ++ assert(s); ++ ++ service_unwatch_control_pid(s); ++ ++ s->control_command = s->exec_command[SERVICE_EXEC_CONDITION]; ++ if (s->control_command) { ++ ++ unit_warn_leftover_processes(UNIT(s)); ++ ++ s->control_command_id = SERVICE_EXEC_CONDITION; ++ ++ r = service_spawn(s, ++ s->control_command, ++ s->timeout_start_usec, ++ EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, ++ &s->control_pid); ++ ++ if (r < 0) ++ goto fail; ++ ++ service_set_state(s, SERVICE_CONDITION); ++ } else ++ service_enter_start_pre(s); ++ ++ return; ++ ++fail: ++ log_unit_warning_errno(UNIT(s), r, "Failed to run 'exec-condition' task: %m"); ++ service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); ++} ++ + static void service_enter_restart(Service *s) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; +@@ -2222,7 +2267,7 @@ static void service_run_next_control(Service *s) { + s->control_command = s->control_command->command_next; + service_unwatch_control_pid(s); + +- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) ++ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) + timeout = s->timeout_start_usec; + else + timeout = s->timeout_stop_usec; +@@ -2231,7 +2276,7 @@ static void service_run_next_control(Service *s) { + s->control_command, + timeout, + EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL| +- (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| ++ (IN_SET(s->control_command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| + (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0), + &s->control_pid); + if (r < 0) +@@ -2242,7 +2287,7 @@ static void service_run_next_control(Service *s) { + fail: + log_unit_warning_errno(UNIT(s), r, "Failed to run next control task: %m"); + +- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START_POST, SERVICE_STOP)) ++ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START_POST, SERVICE_STOP)) + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); + else if (s->state == SERVICE_STOP_POST) + service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); +@@ -2296,7 +2341,7 @@ static int service_start(Unit *u) { + return -EAGAIN; + + /* Already on it! */ +- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST)) ++ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST)) + return 0; + + /* A service that will be restarted must be stopped first to +@@ -2344,7 +2389,9 @@ static int service_start(Unit *u) { + s->flush_n_restarts = false; + } + +- service_enter_start_pre(s); ++ u->reset_accounting = true; ++ ++ service_enter_condition(s); + return 1; + } + +@@ -2370,7 +2417,7 @@ static int service_stop(Unit *u) { + + /* If there's already something running we go directly into + * kill mode. */ +- if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD)) { ++ if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD)) { + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS); + return 0; + } +@@ -3303,6 +3350,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + } else if (s->control_pid == pid) { + s->control_pid = 0; + ++ /* ExecCondition= calls that exit with (0, 254] should invoke skip-like behavior instead of failing */ ++ if (f == SERVICE_FAILURE_EXIT_CODE && s->state == SERVICE_CONDITION && status < 255) ++ f = SERVICE_SKIP_CONDITION; ++ + if (s->control_command) { + exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); + +@@ -3338,6 +3389,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + + switch (s->state) { + ++ case SERVICE_CONDITION: ++ if (f == SERVICE_SUCCESS) ++ service_enter_start_pre(s); ++ else ++ service_enter_signal(s, SERVICE_STOP_SIGTERM, f); ++ break; ++ + case SERVICE_START_PRE: + if (f == SERVICE_SUCCESS) + service_enter_start(s); +@@ -3462,9 +3520,10 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us + + switch (s->state) { + ++ case SERVICE_CONDITION: + case SERVICE_START_PRE: + case SERVICE_START: +- log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", s->state == SERVICE_START ? "Start" : "Start-pre"); ++ log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", service_state_to_string(s->state)); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); + break; + +@@ -3975,6 +4034,7 @@ static bool service_needs_console(Unit *u) { + return false; + + return IN_SET(s->state, ++ SERVICE_CONDITION, + SERVICE_START_PRE, + SERVICE_START, + SERVICE_START_POST, +@@ -4014,6 +4074,7 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { + DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); + + static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = { ++ [SERVICE_EXEC_CONDITION] = "ExecCondition", + [SERVICE_EXEC_START_PRE] = "ExecStartPre", + [SERVICE_EXEC_START] = "ExecStart", + [SERVICE_EXEC_START_POST] = "ExecStartPost", +@@ -4043,6 +4104,7 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { + [SERVICE_FAILURE_CORE_DUMP] = "core-dump", + [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit", ++ [SERVICE_SKIP_CONDITION] = "exec-condition", + }; + + DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); +@@ -4118,6 +4180,7 @@ const UnitVTable service_vtable = { + .finished_start_job = { + [JOB_DONE] = "Started %s.", + [JOB_FAILED] = "Failed to start %s.", ++ [JOB_SKIPPED] = "Skipped %s.", + }, + .finished_stop_job = { + [JOB_DONE] = "Stopped %s.", +diff --git a/src/core/service.h b/src/core/service.h +index 1206e3cdda..62b78cadf1 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -36,6 +36,7 @@ typedef enum ServiceType { + } ServiceType; + + typedef enum ServiceExecCommand { ++ SERVICE_EXEC_CONDITION, + SERVICE_EXEC_START_PRE, + SERVICE_EXEC_START, + SERVICE_EXEC_START_POST, +@@ -67,6 +68,7 @@ typedef enum ServiceResult { + SERVICE_FAILURE_CORE_DUMP, + SERVICE_FAILURE_WATCHDOG, + SERVICE_FAILURE_START_LIMIT_HIT, ++ SERVICE_SKIP_CONDITION, + _SERVICE_RESULT_MAX, + _SERVICE_RESULT_INVALID = -1 + } ServiceResult; +diff --git a/src/core/unit.c b/src/core/unit.c +index ccb0106719..61799bf9e3 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -2227,6 +2227,7 @@ static void unit_update_on_console(Unit *u) { + + static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) { + bool unexpected = false; ++ JobResult result; + + assert(j); + +@@ -2249,8 +2250,16 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) + else if (j->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { + unexpected = true; + +- if (UNIT_IS_INACTIVE_OR_FAILED(ns)) +- job_finish_and_invalidate(j, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); ++ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { ++ if (ns == UNIT_FAILED) ++ result = JOB_FAILED; ++ else if (FLAGS_SET(flags, UNIT_NOTIFY_SKIP_CONDITION)) ++ result = JOB_SKIPPED; ++ else ++ result = JOB_DONE; ++ ++ job_finish_and_invalidate(j, result, true, false); ++ } + } + + break; +@@ -5484,6 +5493,18 @@ void unit_log_failure(Unit *u, const char *result) { + "UNIT_RESULT=%s", result); + } + ++void unit_log_skip(Unit *u, const char *result) { ++ assert(u); ++ assert(result); ++ ++ log_struct(LOG_INFO, ++ "MESSAGE_ID=" SD_MESSAGE_UNIT_SKIPPED_STR, ++ LOG_UNIT_ID(u), ++ LOG_UNIT_INVOCATION_ID(u), ++ LOG_UNIT_MESSAGE(u, "Skipped due to '%s'.", result), ++ "UNIT_RESULT=%s", result); ++} ++ + 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 4ae1b38624..39179f5fd4 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -658,6 +658,7 @@ int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t cont + typedef enum UnitNotifyFlags { + UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0, + UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1, ++ UNIT_NOTIFY_SKIP_CONDITION = 1 << 2, + } UnitNotifyFlags; + + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags); +@@ -806,6 +807,9 @@ int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error); + + void unit_log_success(Unit *u); + void unit_log_failure(Unit *u, const char *result); ++/* unit_log_skip is for cases like ExecCondition= where a unit is considered "done" ++ * after some execution, rather than succeeded or failed. */ ++void unit_log_skip(Unit *u, const char *result); + + /* Macros which append UNIT= or USER_UNIT= to the message */ + +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 8f3b463c6b..e0b2cfb170 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -1334,7 +1334,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con + return bus_append_safe_atou(m, field, eq); + + if (STR_IN_SET(field, +- "ExecStartPre", "ExecStart", "ExecStartPost", ++ "ExecCondition", "ExecStartPre", "ExecStart", "ExecStartPost", + "ExecReload", "ExecStop", "ExecStopPost")) + + return bus_append_exec_command(m, field, eq); +diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h +index e7ef81b597..bdd4fd3974 100644 +--- a/src/systemd/sd-messages.h ++++ b/src/systemd/sd-messages.h +@@ -111,6 +111,8 @@ _SD_BEGIN_DECLARATIONS; + #define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) + #define SD_MESSAGE_UNIT_FAILURE_RESULT_STR \ + SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c) ++#define SD_MESSAGE_UNIT_SKIPPED SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73) ++#define SD_MESSAGE_UNIT_SKIPPED_STR SD_ID128_MAKE_STR(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73) + + #define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + #define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index e42d0d30a8..882e866ea9 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -30,7 +30,7 @@ + + typedef void (*test_function_t)(Manager *m); + +-static void check(const char *func, Manager *m, Unit *unit, int status_expected, int code_expected) { ++static void wait_for_service_finish(Manager *m, Unit *unit) { + Service *service = NULL; + usec_t ts; + usec_t timeout = 2 * USEC_PER_MINUTE; +@@ -55,6 +55,17 @@ static void check(const char *func, Manager *m, Unit *unit, int status_expected, + exit(EXIT_FAILURE); + } + } ++} ++ ++static void check_main_result(const char *func, Manager *m, Unit *unit, int status_expected, int code_expected) { ++ Service *service = NULL; ++ ++ assert_se(m); ++ assert_se(unit); ++ ++ wait_for_service_finish(m, unit); ++ ++ service = SERVICE(unit); + exec_status_dump(&service->main_exec_status, stdout, "\t"); + if (service->main_exec_status.status != status_expected) { + log_error("%s: %s: exit status %d, expected %d", +@@ -70,6 +81,25 @@ static void check(const char *func, Manager *m, Unit *unit, int status_expected, + } + } + ++static void check_service_result(const char *func, Manager *m, Unit *unit, ServiceResult result_expected) { ++ Service *service = NULL; ++ ++ assert_se(m); ++ assert_se(unit); ++ ++ wait_for_service_finish(m, unit); ++ ++ service = SERVICE(unit); ++ ++ if (service->result != result_expected) { ++ log_error("%s: %s: service end result %s, expected %s", ++ func, unit->id, ++ service_result_to_string(service->result), ++ service_result_to_string(result_expected)); ++ abort(); ++ } ++} ++ + static bool check_nobody_user_and_group(void) { + static int cache = -1; + struct passwd *p; +@@ -140,7 +170,17 @@ static void test(const char *func, Manager *m, const char *unit_name, int status + + assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0); + assert_se(unit_start(unit) >= 0); +- check(func, m, unit, status_expected, code_expected); ++ check_main_result(func, m, unit, status_expected, code_expected); ++} ++ ++static void test_service(const char *func, Manager *m, const char *unit_name, ServiceResult result_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_service_result(func, m, unit, result_expected); + } + + static void test_exec_bindpaths(Manager *m) { +@@ -669,6 +709,11 @@ static void test_exec_standardoutput_append(Manager *m) { + test(__func__, m, "exec-standardoutput-append.service", 0, CLD_EXITED); + } + ++static void test_exec_condition(Manager *m) { ++ test_service(__func__, m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE); ++ test_service(__func__, m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION); ++} ++ + typedef struct test_entry { + test_function_t f; + const char *name; +@@ -709,6 +754,7 @@ int main(int argc, char *argv[]) { + entry(test_exec_ambientcapabilities), + entry(test_exec_bindpaths), + entry(test_exec_capabilityboundingset), ++ entry(test_exec_condition), + entry(test_exec_cpuaffinity), + entry(test_exec_environment), + entry(test_exec_environmentfile), +diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +index eab1820e20..9d0530df72 100644 +--- a/test/fuzz/fuzz-unit-file/directives.service ++++ b/test/fuzz/fuzz-unit-file/directives.service +@@ -83,6 +83,7 @@ DirectoryNotEmpty= + Documentation= + DynamicUser= + ExecReload= ++ExecCondition= + ExecStart= + ExecStartPost= + ExecStartPre= +diff --git a/test/meson.build b/test/meson.build +index 4d1c51048c..070731c4a9 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -42,6 +42,8 @@ test_data_files = ''' + test-execute/exec-capabilityboundingset-merge.service + test-execute/exec-capabilityboundingset-reset.service + test-execute/exec-capabilityboundingset-simple.service ++ test-execute/exec-condition-failed.service ++ test-execute/exec-condition-skip.service + test-execute/exec-cpuaffinity1.service + test-execute/exec-cpuaffinity2.service + test-execute/exec-cpuaffinity3.service +diff --git a/test/test-execute/exec-condition-failed.service b/test/test-execute/exec-condition-failed.service +new file mode 100644 +index 0000000000..4a406dc17f +--- /dev/null ++++ b/test/test-execute/exec-condition-failed.service +@@ -0,0 +1,11 @@ ++[Unit] ++Description=Test for exec condition that fails the unit ++ ++[Service] ++Type=oneshot ++ ++# exit 255 will fail the unit ++ExecCondition=/bin/sh -c 'exit 255' ++ ++# This should not get run ++ExecStart=/bin/sh -c 'true' +diff --git a/test/test-execute/exec-condition-skip.service b/test/test-execute/exec-condition-skip.service +new file mode 100644 +index 0000000000..9450e8442a +--- /dev/null ++++ b/test/test-execute/exec-condition-skip.service +@@ -0,0 +1,15 @@ ++[Unit] ++Description=Test for exec condition that triggers skipping ++ ++[Service] ++Type=oneshot ++ ++# exit codes [1, 254] will result in skipping the rest of execution ++ExecCondition=/bin/sh -c 'exit 0' ++ExecCondition=/bin/sh -c 'exit 254' ++ ++# This would normally fail the unit but will not get run due to the skip above ++ExecCondition=/bin/sh -c 'exit 255' ++ ++# This should not get run ++ExecStart=/bin/sh -c 'true' diff --git a/SOURCES/0385-Drop-support-for-lz4-1.3.0.patch b/SOURCES/0385-Drop-support-for-lz4-1.3.0.patch new file mode 100644 index 0000000..0f14b3d --- /dev/null +++ b/SOURCES/0385-Drop-support-for-lz4-1.3.0.patch @@ -0,0 +1,75 @@ +From 09c96d5ef3f2b0bc4e5f1cf69e9b66248e325509 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 29 Oct 2018 18:32:51 +0100 +Subject: [PATCH] Drop support for lz4 < 1.3.0 + +lz4-r130 was released on May 29th, 2015. Let's drop the work-around for older +versions. In particular, we won't test any new code against those ancient +releases, so we shouldn't pretend they are supported. + +(cherry picked from commit e0a1d4b049e6991919a0eacd5d96f7f39dc6ddd1) +Resolves: #1843871 +--- + README | 2 +- + meson.build | 1 + + src/journal/compress.c | 4 ---- + src/journal/test-compress.c | 4 ---- + 4 files changed, 2 insertions(+), 9 deletions(-) + +diff --git a/README b/README +index 7d06e04800..859152fbde 100644 +--- a/README ++++ b/README +@@ -148,7 +148,7 @@ REQUIREMENTS: + libacl (optional) + libselinux (optional) + liblzma (optional) +- liblz4 >= 119 (optional) ++ liblz4 >= 1.3.0 / 130 (optional) + libgcrypt (optional) + libqrencode (optional) + libmicrohttpd (optional) +diff --git a/meson.build b/meson.build +index 70811c29cf..c8ae1e15bd 100644 +--- a/meson.build ++++ b/meson.build +@@ -1076,6 +1076,7 @@ conf.set10('HAVE_XZ', have) + want_lz4 = get_option('lz4') + if want_lz4 != 'false' and not fuzzer_build + liblz4 = dependency('liblz4', ++ version : '>= 1.3.0', + required : want_lz4 == 'true') + have = liblz4.found() + else +diff --git a/src/journal/compress.c b/src/journal/compress.c +index 6baf15c8ff..a4a5e63840 100644 +--- a/src/journal/compress.c ++++ b/src/journal/compress.c +@@ -95,11 +95,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, + if (src_size < 9) + return -ENOBUFS; + +-#if LZ4_VERSION_NUMBER >= 10700 + r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); +-#else +- r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); +-#endif + if (r <= 0) + return -ENOBUFS; + +diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c +index 791c6fdffb..eb3dc3eb6b 100644 +--- a/src/journal/test-compress.c ++++ b/src/journal/test-compress.c +@@ -207,11 +207,7 @@ static void test_lz4_decompress_partial(void) { + memset(huge, 'x', HUGE_SIZE); + memcpy(huge, "HUGE=", 5); + +-#if LZ4_VERSION_NUMBER >= 10700 + r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size); +-#else +- r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size); +-#endif + assert_se(r >= 0); + compressed = r; + log_info("Compressed %i → %zu", HUGE_SIZE, compressed); diff --git a/SOURCES/0386-test-compress-add-test-for-short-decompress_startswi.patch b/SOURCES/0386-test-compress-add-test-for-short-decompress_startswi.patch new file mode 100644 index 0000000..2931a78 --- /dev/null +++ b/SOURCES/0386-test-compress-add-test-for-short-decompress_startswi.patch @@ -0,0 +1,72 @@ +From fc1e6209f622ff96c24259a50d98ca6f57a55426 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 29 Oct 2018 22:21:28 +0100 +Subject: [PATCH] test-compress: add test for short decompress_startswith calls + +I thought this might fail with lz4 < 1.8.3, but it seems that because of +greedy_realloc, we always use a buffer that is large enough, and it always +passes. + +(cherry picked from commit ba17efce44e6a1e139c1671205e9a6ed3824af1b) +Resolves: #1843871 +--- + src/journal/test-compress.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c +index eb3dc3eb6b..65cd3fbfeb 100644 +--- a/src/journal/test-compress.c ++++ b/src/journal/test-compress.c +@@ -131,6 +131,32 @@ static void test_decompress_startswith(int compression, + assert_se(r > 0); + } + ++static void test_decompress_startswith_short(int compression, ++ compress_blob_t compress, ++ decompress_sw_t decompress_sw) { ++ ++#define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ++ ++ char buf[1024]; ++ size_t i, csize; ++ int r; ++ ++ log_info("/* %s with %s */", __func__, object_compressed_to_string(compression)); ++ ++ r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize); ++ assert_se(r == 0); ++ ++ for (i = 1; i < strlen(TEXT); i++) { ++ size_t alloc_size = i; ++ _cleanup_free_ void *buf2 = NULL; ++ ++ assert_se(buf2 = malloc(i)); ++ ++ assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, TEXT[i]) == 1); ++ assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, 'y') == 0); ++ } ++} ++ + static void test_compress_stream(int compression, + const char* cat, + compress_stream_t compress, +@@ -271,6 +297,9 @@ int main(int argc, char *argv[]) { + + test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat", + compress_stream_xz, decompress_stream_xz, srcfile); ++ ++ test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz); ++ + #else + log_info("/* XZ test skipped */"); + #endif +@@ -295,6 +324,9 @@ int main(int argc, char *argv[]) { + compress_stream_lz4, decompress_stream_lz4, srcfile); + + test_lz4_decompress_partial(); ++ ++ test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4); ++ + #else + log_info("/* LZ4 test skipped */"); + #endif diff --git a/SOURCES/0387-journal-adapt-for-new-improved-LZ4_decompress_safe_p.patch b/SOURCES/0387-journal-adapt-for-new-improved-LZ4_decompress_safe_p.patch new file mode 100644 index 0000000..2b61c2c --- /dev/null +++ b/SOURCES/0387-journal-adapt-for-new-improved-LZ4_decompress_safe_p.patch @@ -0,0 +1,180 @@ +From 696d56fc75e72f47e4d3232a2140fac10b6b44de Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 29 Oct 2018 14:55:33 +0100 +Subject: [PATCH] journal: adapt for new improved LZ4_decompress_safe_partial() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With lz4 1.8.3, this function can now decompress partial results into a smaller +buffer. The release news don't say anything interesting, but the test case that +was previously failing now works OK. + +Fixes #10259. + +A test is added. It shows that with *older* lz4, a partial decompression can +occur with the returned size smaller then the requested number of bytes _and_ +smaller then the size of the compressed data: + +(lz4-libs-1.8.2-1.fc29.x86_64) +Compressed 4194304 → 16464 +Decompressed → 4194304 +Decompressed partial 12/4194304 → 4194304 +Decompressed partial 1/1 → -2 (bad) +Decompressed partial 2/2 → -2 (bad) +Decompressed partial 3/3 → -2 (bad) +Decompressed partial 4/4 → -2 (bad) +Decompressed partial 5/5 → -2 (bad) +Decompressed partial 6/6 → 6 (good) +Decompressed partial 7/7 → 6 (good) +Decompressed partial 8/8 → 6 (good) +Decompressed partial 9/9 → 6 (good) +Decompressed partial 10/10 → 6 (good) +Decompressed partial 11/11 → 6 (good) +Decompressed partial 12/12 → 6 (good) +Decompressed partial 13/13 → 6 (good) +Decompressed partial 14/14 → 6 (good) +Decompressed partial 15/15 → 6 (good) +Decompressed partial 16/16 → 6 (good) +Decompressed partial 17/17 → 6 (good) +Decompressed partial 18/18 → -16459 (bad) + +(lz4-libs-1.8.3-1.fc29.x86_64) +Compressed 4194304 → 16464 +Decompressed → 4194304 +Decompressed partial 12/4194304 → 12 +Decompressed partial 1/1 → 1 (good) +Decompressed partial 2/2 → 2 (good) +Decompressed partial 3/3 → 3 (good) +Decompressed partial 4/4 → 4 (good) +... + +If we got such a short "successful" decompression in decompress_startswith() as +implemented before this patch, we could be confused and return a false negative +result. But it turns out that this only occurs with small output buffer +sizes. We use greedy_realloc() to manager the buffer, so it is always at least +64 bytes. I couldn't hit a case where decompress_startswith() would actually +return a bogus result. But since the lack of proof is not conclusive, the code +for *older* lz4 is changed too, just to be safe. We cannot rule out that on a +different architecture or with some unlucky compressed string we could hit this +corner case. + +The fallback code is guarded by a version check. The check uses a function not +the compile-time define, because there was no soversion bump in lz4 or new +symbols, and we could be compiled against a newer lz4 and linked at runtime +with an older one. (This happens routinely e.g. when somebody upgrades a subset +of distro packages.) + +(cherry picked from commit e41ef6fd0027d3619dc1cf062100b2d224d0ee7e) +Resolves: #1843871 +--- + src/journal/compress.c | 39 ++++++++++++++++++++++++------------- + src/journal/test-compress.c | 21 ++++++++++---------- + 2 files changed, 37 insertions(+), 23 deletions(-) + +diff --git a/src/journal/compress.c b/src/journal/compress.c +index a4a5e63840..e95ce2bcaa 100644 +--- a/src/journal/compress.c ++++ b/src/journal/compress.c +@@ -290,7 +290,6 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, + * prefix */ + + int r; +- size_t size; + + assert(src); + assert(src_size > 0); +@@ -307,23 +306,37 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, + + r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8, + prefix_len + 1, *buffer_size); +- if (r >= 0) +- size = (unsigned) r; +- else { +- /* lz4 always tries to decode full "sequence", so in +- * pathological cases might need to decompress the +- * full field. */ ++ /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where ++ * just a part of the buffer is decompressed. But if we get a smaller ++ * amount of bytes than requested, we don't know whether there isn't enough ++ * data to fill the requested size or whether we just got a partial answer. ++ */ ++ if (r < 0 || (size_t) r < prefix_len + 1) { ++ size_t size; ++ ++ if (LZ4_versionNumber() >= 10803) ++ /* We trust that the newer lz4 decompresses the number of bytes we ++ * requested if available in the compressed string. */ ++ return 0; ++ ++ if (r > 0) ++ /* Compare what we have first, in case of mismatch we can ++ * shortcut the full comparison. */ ++ if (memcmp(*buffer, prefix, r) != 0) ++ return 0; ++ ++ /* Before version 1.8.3, lz4 always tries to decode full a "sequence", ++ * so in pathological cases might need to decompress the full field. */ + r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0); + if (r < 0) + return r; +- } + +- if (size >= prefix_len + 1) +- return memcmp(*buffer, prefix, prefix_len) == 0 && +- ((const uint8_t*) *buffer)[prefix_len] == extra; +- else +- return 0; ++ if (size < prefix_len + 1) ++ return 0; ++ } + ++ return memcmp(*buffer, prefix, prefix_len) == 0 && ++ ((const uint8_t*) *buffer)[prefix_len] == extra; + #else + return -EPROTONOSUPPORT; + #endif +diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c +index 65cd3fbfeb..f60c4ae3d7 100644 +--- a/src/journal/test-compress.c ++++ b/src/journal/test-compress.c +@@ -223,13 +223,13 @@ static void test_compress_stream(int compression, + + #if HAVE_LZ4 + static void test_lz4_decompress_partial(void) { +- char buf[20000]; ++ char buf[20000], buf2[100]; + size_t buf_size = sizeof(buf), compressed; + int r; + _cleanup_free_ char *huge = NULL; + + #define HUGE_SIZE (4096*1024) +- huge = malloc(HUGE_SIZE); ++ assert_se(huge = malloc(HUGE_SIZE)); + memset(huge, 'x', HUGE_SIZE); + memcpy(huge, "HUGE=", 5); + +@@ -248,14 +248,15 @@ static void test_lz4_decompress_partial(void) { + assert_se(r >= 0); + log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r); + +- /* We expect this to fail, because that's how current lz4 works. If this +- * call succeeds, then lz4 has been fixed, and we need to change our code. +- */ +- r = LZ4_decompress_safe_partial(buf, huge, +- compressed, +- 12, HUGE_SIZE-1); +- assert_se(r < 0); +- log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r); ++ for (size_t size = 1; size < sizeof(buf2); size++) { ++ /* This failed in older lz4s but works in newer ones. */ ++ r = LZ4_decompress_safe_partial(buf, buf2, compressed, size, size); ++ log_info("Decompressed partial %zu/%zu → %i (%s)", size, size, r, ++ r < 0 ? "bad" : "good"); ++ if (r >= 0 && LZ4_versionNumber() >= 10803) ++ /* lz4 <= 1.8.2 should fail that test, let's only check for newer ones */ ++ assert_se(memcmp(buf2, huge, r) == 0); ++ } + } + #endif + diff --git a/SOURCES/0388-fuzz-compress-add-fuzzer-for-compression-and-decompr.patch b/SOURCES/0388-fuzz-compress-add-fuzzer-for-compression-and-decompr.patch new file mode 100644 index 0000000..cccc17a --- /dev/null +++ b/SOURCES/0388-fuzz-compress-add-fuzzer-for-compression-and-decompr.patch @@ -0,0 +1,118 @@ +From 242273e1afd456e86ebc48d7d601cb28297f8efb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 30 Oct 2018 09:02:26 +0100 +Subject: [PATCH] fuzz-compress: add fuzzer for compression and decompression + +(cherry picked from commit 029427043b2e0523a21f54374f872b23cf744350) +Resolves: #1843871 +--- + src/fuzz/fuzz-compress.c | 80 ++++++++++++++++++++++++++++++++++++++++ + src/fuzz/meson.build | 7 +++- + 2 files changed, 86 insertions(+), 1 deletion(-) + create mode 100644 src/fuzz/fuzz-compress.c + +diff --git a/src/fuzz/fuzz-compress.c b/src/fuzz/fuzz-compress.c +new file mode 100644 +index 0000000000..9c5dfc92c0 +--- /dev/null ++++ b/src/fuzz/fuzz-compress.c +@@ -0,0 +1,80 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++ ++#include "alloc-util.h" ++#include "compress.h" ++#include "fuzz.h" ++ ++static int compress(int alg, ++ const void *src, uint64_t src_size, ++ void *dst, size_t dst_alloc_size, size_t *dst_size) { ++ ++ if (alg == OBJECT_COMPRESSED_LZ4) ++ return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size); ++ if (alg == OBJECT_COMPRESSED_XZ) ++ return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size); ++ return -EOPNOTSUPP; ++} ++ ++typedef struct header { ++ uint32_t alg:2; /* We have only two compression algorithms so far, but we might add ++ * more in the future. Let's make this a bit wider so our fuzzer ++ * cases remain stable in the future. */ ++ uint32_t sw_len; ++ uint32_t sw_alloc; ++ uint32_t reserved[3]; /* Extra space to keep fuzz cases stable in case we need to ++ * add stuff in the future. */ ++ uint8_t data[]; ++} header; ++ ++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ++ _cleanup_free_ void *buf = NULL, *buf2 = NULL; ++ int r; ++ ++ if (size < offsetof(header, data) + 1) ++ return 0; ++ ++ const header *h = (struct header*) data; ++ const size_t data_len = size - offsetof(header, data); ++ ++ int alg = h->alg; ++ ++ /* We don't want to fill the logs with messages about parse errors. ++ * Disable most logging if not running standalone */ ++ if (!getenv("SYSTEMD_LOG_LEVEL")) ++ log_set_max_level(LOG_CRIT); ++ ++ log_info("Using compression %s, data size=%zu", ++ object_compressed_to_string(alg) ?: "(none)", ++ data_len); ++ ++ buf = malloc(MAX(size, 128u)); /* Make the buffer a bit larger for very small data */ ++ if (!buf) { ++ log_oom(); ++ return 0; ++ } ++ ++ size_t csize; ++ r = compress(alg, h->data, data_len, buf, size, &csize); ++ if (r < 0) { ++ log_error_errno(r, "Compression failed: %m"); ++ return 0; ++ } ++ ++ log_debug("Compressed %zu bytes to → %zu bytes", data_len, csize); ++ ++ size_t sw_alloc = MAX(h->sw_alloc, 1u); ++ buf2 = malloc(sw_alloc); ++ if (!buf) { ++ log_oom(); ++ return 0; ++ } ++ ++ size_t sw_len = MIN(data_len - 1, h->sw_len); ++ ++ r = decompress_startswith(alg, buf, csize, &buf2, &sw_alloc, h->data, sw_len, h->data[sw_len]); ++ assert_se(r > 0); ++ ++ return 0; ++} +diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build +index 5315d2771c..b8d5979d3c 100644 +--- a/src/fuzz/meson.build ++++ b/src/fuzz/meson.build +@@ -73,8 +73,13 @@ fuzzers += [ + [libsystemd_journal_remote, + libshared], + []], ++ + [['src/fuzz/fuzz-fido-id-desc.c', + 'src/udev/fido_id/fido_id_desc.c'], + [], +- []] ++ []], ++ ++ [['src/fuzz/fuzz-compress.c'], ++ [libshared], ++ []], + ] diff --git a/SOURCES/0389-seccomp-fix-__NR__sysctl-usage.patch b/SOURCES/0389-seccomp-fix-__NR__sysctl-usage.patch new file mode 100644 index 0000000..7d67d8f --- /dev/null +++ b/SOURCES/0389-seccomp-fix-__NR__sysctl-usage.patch @@ -0,0 +1,35 @@ +From 65a066aae68744e889c114cee56dff5b48d872df Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Thu, 4 Jun 2020 16:55:52 +0200 +Subject: [PATCH] seccomp: fix __NR__sysctl usage + +Loosely based on +https://github.com/systemd/systemd/pull/14032 and +https://github.com/systemd/systemd/pull/14268. + +Related: #1843871 +--- + src/test/test-seccomp.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c +index 4021a06e0e..009a2e1922 100644 +--- a/src/test/test-seccomp.c ++++ b/src/test/test-seccomp.c +@@ -237,14 +237,14 @@ static void test_protect_sysctl(void) { + assert_se(pid >= 0); + + if (pid == 0) { +-#if __NR__sysctl > 0 ++#if defined __NR__sysctl && __NR__sysctl >= 0 + assert_se(syscall(__NR__sysctl, NULL) < 0); + assert_se(errno == EFAULT); + #endif + + assert_se(seccomp_protect_sysctl() >= 0); + +-#if __NR__sysctl > 0 ++#if defined __NR__sysctl && __NR__sysctl >= 0 + assert_se(syscall(__NR__sysctl, 0, 0, 0) < 0); + assert_se(errno == EPERM); + #endif diff --git a/SOURCES/0390-tmpfiles-fix-crash-with-NULL-in-arg_root-and-other-f.patch b/SOURCES/0390-tmpfiles-fix-crash-with-NULL-in-arg_root-and-other-f.patch new file mode 100644 index 0000000..8ae24a5 --- /dev/null +++ b/SOURCES/0390-tmpfiles-fix-crash-with-NULL-in-arg_root-and-other-f.patch @@ -0,0 +1,174 @@ +From 3569b29eb8b082229dd97b8aae60bbe4d2f96ef5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 19 Dec 2018 23:05:48 +0100 +Subject: [PATCH] tmpfiles: fix crash with NULL in arg_root and other fixes and + tests + +The function to replacement paths into the configuration file list was borked. +Apart from the crash with empty root prefix, it would incorrectly handle the +case where root *was* set, and the replacement file was supposed to override +an existing file. + +prefix_root is used instead of path_join because prefix_root removes duplicate +slashes (when --root=dir/ is used). + +A test is added. + +Fixes #11124. + +(cherry picked from commit 082bb1c59bd4300bcdc08488c94109680cfadf57) + +Resolves: #1836024 +--- + src/basic/conf-files.c | 21 ++++++++----- + src/test/test-conf-files.c | 61 +++++++++++++++++++++++++++++++++++++- + 2 files changed, 73 insertions(+), 9 deletions(-) + +diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c +index d6ef0e941e..5ca83091c9 100644 +--- a/src/basic/conf-files.c ++++ b/src/basic/conf-files.c +@@ -204,14 +204,17 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p + if (c == 0) { + char **dir; + +- /* Oh, we found our spot and it already contains something. */ ++ /* Oh, there already is an entry with a matching name (the last component). */ ++ + STRV_FOREACH(dir, dirs) { ++ _cleanup_free_ char *rdir = NULL; + char *p1, *p2; + +- p1 = path_startswith((*strv)[i], root); +- if (p1) +- /* Skip "/" in *dir, because p1 is without "/" too */ +- p1 = path_startswith(p1, *dir + 1); ++ rdir = prefix_root(root, *dir); ++ if (!rdir) ++ return -ENOMEM; ++ ++ p1 = path_startswith((*strv)[i], rdir); + if (p1) + /* Existing entry with higher priority + * or same priority, no need to do anything. */ +@@ -220,7 +223,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p + p2 = path_startswith(path, *dir); + if (p2) { + /* Our new entry has higher priority */ +- t = path_join(root, path, NULL); ++ ++ t = prefix_root(root, path); + if (!t) + return log_oom(); + +@@ -236,7 +240,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p + /* … we are not there yet, let's continue */ + } + +- t = path_join(root, path, NULL); ++ /* The new file has lower priority than all the existing entries */ ++ t = prefix_root(root, path); + if (!t) + return log_oom(); + +@@ -322,7 +327,7 @@ int conf_files_list_with_replacement( + if (r < 0) + return log_error_errno(r, "Failed to extend config file list: %m"); + +- p = path_join(root, replacement, NULL); ++ p = prefix_root(root, replacement); + if (!p) + return log_oom(); + } +diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c +index 2ec2dfc261..5789767161 100644 +--- a/src/test/test-conf-files.c ++++ b/src/test/test-conf-files.c +@@ -13,6 +13,7 @@ + #include "macro.h" + #include "mkdir.h" + #include "parse-util.h" ++#include "path-util.h" + #include "rm-rf.h" + #include "string-util.h" + #include "strv.h" +@@ -42,7 +43,7 @@ static void test_conf_files_list(bool use_root) { + _cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL; + const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c, *mask; + +- log_debug("/* %s */", __func__); ++ log_debug("/* %s(%s) */", __func__, yes_no(use_root)); + + setup_test_dir(tmp_dir, + "/dir1/a.conf", +@@ -92,6 +93,60 @@ static void test_conf_files_list(bool use_root) { + assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); + } + ++static void test_conf_files_insert(const char *root) { ++ _cleanup_strv_free_ char **s = NULL; ++ ++ log_info("/* %s root=%s */", __func__, strempty(root)); ++ ++ char **dirs = STRV_MAKE("/dir1", "/dir2", "/dir3"); ++ ++ _cleanup_free_ const char ++ *foo1 = prefix_root(root, "/dir1/foo.conf"), ++ *foo2 = prefix_root(root, "/dir2/foo.conf"), ++ *bar2 = prefix_root(root, "/dir2/bar.conf"), ++ *zzz3 = prefix_root(root, "/dir3/zzz.conf"), ++ *whatever = prefix_root(root, "/whatever.conf"); ++ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(foo2))); ++ ++ /* The same file again, https://github.com/systemd/systemd/issues/11124 */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(foo2))); ++ ++ /* Lower priority → new entry is ignored */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir3/foo.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(foo2))); ++ ++ /* Higher priority → new entry replaces */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir1/foo.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(foo1))); ++ ++ /* Earlier basename */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir2/bar.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1))); ++ ++ /* Later basename */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir3/zzz.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3))); ++ ++ /* All lower priority → all ignored */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir3/zzz.conf") == 0); ++ assert_se(conf_files_insert(&s, root, dirs, "/dir2/bar.conf") == 0); ++ assert_se(conf_files_insert(&s, root, dirs, "/dir3/bar.conf") == 0); ++ assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3))); ++ ++ /* Two entries that don't match any of the directories, but match basename */ ++ assert_se(conf_files_insert(&s, root, dirs, "/dir4/zzz.conf") == 0); ++ assert_se(conf_files_insert(&s, root, dirs, "/zzz.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3))); ++ ++ /* An entry that doesn't match any of the directories, no match at all */ ++ assert_se(conf_files_insert(&s, root, dirs, "/whatever.conf") == 0); ++ assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, whatever, zzz3))); ++} ++ + int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); +@@ -99,5 +154,9 @@ int main(int argc, char **argv) { + + test_conf_files_list(false); + test_conf_files_list(true); ++ test_conf_files_insert(NULL); ++ test_conf_files_insert("/root"); ++ test_conf_files_insert("/root/"); ++ + return 0; + } diff --git a/SOURCES/0391-sulogin-shell-Use-force-if-SYSTEMD_SULOGIN_FORCE-set.patch b/SOURCES/0391-sulogin-shell-Use-force-if-SYSTEMD_SULOGIN_FORCE-set.patch new file mode 100644 index 0000000..31783e0 --- /dev/null +++ b/SOURCES/0391-sulogin-shell-Use-force-if-SYSTEMD_SULOGIN_FORCE-set.patch @@ -0,0 +1,85 @@ +From d8ae33a302f01601e9e98b4aca3516e93c634a54 Mon Sep 17 00:00:00 2001 +From: Andreas Henriksson +Date: Sun, 14 Oct 2018 14:53:09 +0200 +Subject: [PATCH] sulogin-shell: Use force if SYSTEMD_SULOGIN_FORCE set + +When the root account is locked sulogin will either inform you of +this and not allow you in or if --force is used it will hand +you passwordless root (if using a recent enough version of util-linux). + +Not being allowed a shell is ofcourse inconvenient, but at the same +time handing out passwordless root unconditionally is probably not +a good idea everywhere. + +This patch thus allows to control which behaviour you want by +setting the SYSTEMD_SULOGIN_FORCE environment variable to true +or false to control the behaviour, eg. via adding this to +'systemctl edit rescue.service' (or emergency.service): + +[Service] +Environment=SYSTEMD_SULOGIN_FORCE=1 + +Distributions who used locked root accounts and want the passwordless +behaviour could thus simply drop in the override file in +/etc/systemd/system/rescue.service.d/override.conf + +Fixes: #7115 +Addresses: https://bugs.debian.org/802211 +(cherry picked from commit 33eb44fe4a8d7971b5614bc4c2d90f8d91cce66c) + +Resolves: #1625929 +--- + doc/ENVIRONMENT.md | 6 ++++++ + src/sulogin-shell/sulogin-shell.c | 11 ++++++++++- + 2 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md +index 1e648be640..39a36a52cc 100644 +--- a/doc/ENVIRONMENT.md ++++ b/doc/ENVIRONMENT.md +@@ -101,3 +101,9 @@ systemd-timedated: + NTP client services. If set, `timedatectl set-ntp on` enables and starts the + first existing unit listed in the environment variable, and + `timedatectl set-ntp off` disables and stops all listed units. ++ ++systemd-sulogin-shell: ++ ++* `$SYSTEMD_SULOGIN_FORCE=1` — This skips asking for the root password if the ++ root password is not available (such as when the root account is locked). ++ See `sulogin(8)` for more details. +diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c +index 5db3592d6f..a1ea2333de 100644 +--- a/src/sulogin-shell/sulogin-shell.c ++++ b/src/sulogin-shell/sulogin-shell.c +@@ -9,6 +9,7 @@ + #include "bus-util.h" + #include "bus-error.h" + #include "def.h" ++#include "env-util.h" + #include "log.h" + #include "process-util.h" + #include "sd-bus.h" +@@ -89,7 +90,11 @@ static void print_mode(const char* mode) { + } + + int main(int argc, char *argv[]) { +- static const char* const sulogin_cmdline[] = {SULOGIN, NULL}; ++ const char* sulogin_cmdline[] = { ++ SULOGIN, ++ NULL, /* --force */ ++ NULL ++ }; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + +@@ -99,6 +104,10 @@ int main(int argc, char *argv[]) { + + print_mode(argc > 1 ? argv[1] : ""); + ++ if (getenv_bool("SYSTEMD_SULOGIN_FORCE") > 0) ++ /* allows passwordless logins if root account is locked. */ ++ sulogin_cmdline[1] = "--force"; ++ + (void) fork_wait(sulogin_cmdline); + + r = bus_connect_system_systemd(&bus); diff --git a/SOURCES/0392-resolvconf-fixes-for-the-compatibility-interface.patch b/SOURCES/0392-resolvconf-fixes-for-the-compatibility-interface.patch new file mode 100644 index 0000000..636b585 --- /dev/null +++ b/SOURCES/0392-resolvconf-fixes-for-the-compatibility-interface.patch @@ -0,0 +1,62 @@ +From 9b7aa39e7db5a6446d3c034741e64cda1a9dd200 Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Mon, 25 Jun 2018 18:07:48 -0700 +Subject: [PATCH] resolvconf: fixes for the compatibility interface + +Also use compat_main() when called as `resolvconf`, since the interface +is closer to that of `systemd-resolve`. + +Use a heap allocated string to set arg_ifname, since a stack allocated +one would be lost after the function returns. (This last one broke the +case where an interface name was suffixed with a dot, such as in +`resolvconf -a tap0.dhcp`.) + +Tested: + $ build/resolvconf -a nonexistent.abc +Date: Tue, 17 Mar 2020 10:49:44 +0100 +Subject: [PATCH] mount: don't add Requires for tmp.mount + +This is a follow-up to #1619292. + +rhel-only +Resolves: #1748840 +--- + src/core/mount.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 30aaf5ae55..2746372db2 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -305,7 +305,7 @@ static int mount_add_mount_dependencies(Mount *m) { + if (r < 0) + return r; + +- if (UNIT(m)->fragment_path) { ++ if (UNIT(m)->fragment_path && !streq(UNIT(m)->id, "tmp.mount")) { + /* If we have fragment configuration, then make this dependency required */ + r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true, UNIT_DEPENDENCY_PATH); + if (r < 0) diff --git a/SOURCES/0394-core-coldplug-possible-nop_job.patch b/SOURCES/0394-core-coldplug-possible-nop_job.patch new file mode 100644 index 0000000..f133544 --- /dev/null +++ b/SOURCES/0394-core-coldplug-possible-nop_job.patch @@ -0,0 +1,55 @@ +From 4a1405998671caaaad5b24d4cef309c05175b1c1 Mon Sep 17 00:00:00 2001 +From: ypf791 +Date: Fri, 19 Jul 2019 18:28:04 +0800 +Subject: [PATCH] core: coldplug possible nop_job + +When a unit in a state INACTIVE or DEACTIVATING, JobType JOB_TRY_RESTART or +JOB_TRY_RELOAD will be collapsed to JOB_NOP. And use u->nop_job instead +of u->job. + +If a JOB_NOP job is going on with a waiting state, a parallel daemon-reload +just install it during deserialization. Without a coldplug, the job will +not be in m->run_queue, which results in a hung try-restart or +try-reload process. + +Reproduce: + + run systemctl try-restart test.servcie (inactive) repeatly in a terminal. + run systemctl daemon-reload repeatly in other terminals. + +After successful reproduce, systemctl list-jobs will list the hang job. + +Upsteam: +systemd/systemd#13124 + +(cherry picked from commit b49e14d5f3081dfcd363d8199a14c0924ae9152f) + +Resolves: #1829798 +--- + src/core/unit.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 61799bf9e3..f57260727f 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -3696,6 +3696,7 @@ int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependen + int unit_coldplug(Unit *u) { + int r = 0, q; + char **i; ++ Job *uj; + + assert(u); + +@@ -3718,8 +3719,9 @@ int unit_coldplug(Unit *u) { + r = q; + } + +- if (u->job) { +- q = job_coldplug(u->job); ++ uj = u->job ?: u->nop_job; ++ if (uj) { ++ q = job_coldplug(uj); + if (q < 0 && r >= 0) + r = q; + } diff --git a/SOURCES/0395-core-add-IODeviceLatencyTargetSec.patch b/SOURCES/0395-core-add-IODeviceLatencyTargetSec.patch new file mode 100644 index 0000000..974a1d9 --- /dev/null +++ b/SOURCES/0395-core-add-IODeviceLatencyTargetSec.patch @@ -0,0 +1,584 @@ +From 2240e7955d64260e94dd52a3ab9855d267c2af89 Mon Sep 17 00:00:00 2001 +From: Tejun Heo +Date: Wed, 13 Jun 2018 14:16:35 -0700 +Subject: [PATCH] core: add IODeviceLatencyTargetSec + +This adds support for the following proposed latency based IO control +mechanism. + + https://lkml.org/lkml/2018/6/5/428 + +(cherry picked from commit 6ae4283cb14c4e4a895f4bbba703804e4128c86c) + +Resolves: #1831519 +--- + man/systemd.resource-control.xml | 29 +++++-- + src/core/cgroup.c | 56 +++++++++++-- + src/core/cgroup.h | 9 +++ + src/core/dbus-cgroup.c | 111 ++++++++++++++++++++++++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/load-fragment.c | 72 +++++++++++++++++ + src/core/load-fragment.h | 1 + + src/shared/bus-unit-util.c | 31 +++++++ + src/systemctl/systemctl.c | 22 +++++ + 9 files changed, 320 insertions(+), 12 deletions(-) + +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index 4329742e94..b0064bf98f 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -417,11 +417,11 @@ + + Set the per-device overall block I/O weight for the executed processes, if the unified control group + hierarchy is used on the system. Takes a space-separated pair of a file path and a weight value to specify +- the device specific weight value, between 1 and 10000. (Example: "/dev/sda 1000"). The file path may be +- specified as path to a block device node or as any other file, in which case the backing block device of the +- file system of the file is determined. This controls the io.weight control group +- attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices. For +- details about this control group attribute, see /dev/sda 1000). The file ++ path may be specified as path to a block device node or as any other file, in which case the backing block ++ device of the file system of the file is determined. This controls the io.weight control ++ group attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices. ++ For details about this control group attribute, see cgroup-v2.txt. + + Implies IOAccounting=true. +@@ -482,6 +482,25 @@ + + + ++ ++ IODeviceLatencyTargetSec=device target ++ ++ ++ Set the per-device average target I/O latency for the executed processes, if the unified control group ++ hierarchy is used on the system. Takes a file path and a timespan separated by a space to specify ++ the device specific latency target. (Example: "/dev/sda 25ms"). The file path may be specified ++ as path to a block device node or as any other file, in which case the backing block device of the file ++ system of the file is determined. This controls the io.latency control group ++ attribute. Use this option multiple times to set latency target for multiple devices. For details about this ++ control group attribute, see cgroup-v2.txt. ++ ++ Implies IOAccounting=true. ++ ++ These settings are supported only if the unified control group hierarchy is used. ++ ++ ++ + + IPAccounting= + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 9e4c3c7dac..ad8219bd79 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -114,6 +114,15 @@ void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight + free(w); + } + ++void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l) { ++ assert(c); ++ assert(l); ++ ++ LIST_REMOVE(device_latencies, c->io_device_latencies, l); ++ free(l->path); ++ free(l); ++} ++ + void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) { + assert(c); + assert(l); +@@ -147,6 +156,9 @@ void cgroup_context_done(CGroupContext *c) { + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + ++ while (c->io_device_latencies) ++ cgroup_context_free_io_device_latency(c, c->io_device_latencies); ++ + while (c->io_device_limits) + cgroup_context_free_io_device_limit(c, c->io_device_limits); + +@@ -171,6 +183,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + _cleanup_free_ char *cpuset_mems = NULL; + CGroupIODeviceLimit *il; + CGroupIODeviceWeight *iw; ++ CGroupIODeviceLatency *l; + CGroupBlockIODeviceBandwidth *b; + CGroupBlockIODeviceWeight *w; + CGroupDeviceAllow *a; +@@ -256,11 +269,18 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + + LIST_FOREACH(device_weights, iw, c->io_device_weights) + fprintf(f, +- "%sIODeviceWeight=%s %" PRIu64, ++ "%sIODeviceWeight=%s %" PRIu64 "\n", + prefix, + iw->path, + iw->weight); + ++ LIST_FOREACH(device_latencies, l, c->io_device_latencies) ++ fprintf(f, ++ "%sIODeviceLatencyTargetSec=%s %s\n", ++ prefix, ++ l->path, ++ format_timespan(u, sizeof(u), l->target_usec, 1)); ++ + LIST_FOREACH(device_limits, il, c->io_device_limits) { + char buf[FORMAT_BYTES_MAX]; + CGroupIOLimitType type; +@@ -573,6 +593,7 @@ static bool cgroup_context_has_io_config(CGroupContext *c) { + c->io_weight != CGROUP_WEIGHT_INVALID || + c->startup_io_weight != CGROUP_WEIGHT_INVALID || + c->io_device_weights || ++ c->io_device_latencies || + c->io_device_limits; + } + +@@ -646,6 +667,26 @@ static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint + "Failed to set blkio.weight_device: %m"); + } + ++static void cgroup_apply_io_device_latency(Unit *u, const char *dev_path, usec_t target) { ++ char buf[DECIMAL_STR_MAX(dev_t)*2+2+7+DECIMAL_STR_MAX(uint64_t)+1]; ++ dev_t dev; ++ int r; ++ ++ r = lookup_block_device(dev_path, &dev); ++ if (r < 0) ++ return; ++ ++ if (target != USEC_INFINITY) ++ xsprintf(buf, "%u:%u target=%" PRIu64 "\n", major(dev), minor(dev), target); ++ else ++ xsprintf(buf, "%u:%u target=max\n", major(dev), minor(dev)); ++ ++ r = cg_set_attribute("io", u->cgroup_path, "io.latency", buf); ++ if (r < 0) ++ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, ++ "Failed to set io.latency on cgroup %s: %m", u->cgroup_path); ++} ++ + static void cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) { + char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)]; + char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4]; +@@ -827,13 +868,11 @@ static void cgroup_context_apply( + if (has_io) { + CGroupIODeviceWeight *w; + +- /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) + cgroup_apply_io_device_weight(u, w->path, w->weight); + } else if (has_blockio) { + CGroupBlockIODeviceWeight *w; + +- /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) { + weight = cgroup_weight_blkio_to_io(w->weight); + +@@ -843,9 +882,15 @@ static void cgroup_context_apply( + cgroup_apply_io_device_weight(u, w->path, weight); + } + } ++ ++ if (has_io) { ++ CGroupIODeviceLatency *l; ++ ++ LIST_FOREACH(device_latencies, l, c->io_device_latencies) ++ cgroup_apply_io_device_latency(u, l->path, l->target_usec); ++ } + } + +- /* Apply limits and free ones without config. */ + if (has_io) { + CGroupIODeviceLimit *l; + +@@ -902,7 +947,6 @@ static void cgroup_context_apply( + if (has_io) { + CGroupIODeviceWeight *w; + +- /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) { + weight = cgroup_weight_io_to_blkio(w->weight); + +@@ -914,13 +958,11 @@ static void cgroup_context_apply( + } else if (has_blockio) { + CGroupBlockIODeviceWeight *w; + +- /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) + cgroup_apply_blkio_device_weight(u, w->path, w->weight); + } + } + +- /* Apply limits and free ones without config. */ + if (has_io) { + CGroupIODeviceLimit *l; + +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index da10575394..f7365b4c46 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -13,6 +13,7 @@ typedef struct CGroupContext CGroupContext; + typedef struct CGroupDeviceAllow CGroupDeviceAllow; + typedef struct CGroupIODeviceWeight CGroupIODeviceWeight; + typedef struct CGroupIODeviceLimit CGroupIODeviceLimit; ++typedef struct CGroupIODeviceLatency CGroupIODeviceLatency; + typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; + typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth; + +@@ -52,6 +53,12 @@ struct CGroupIODeviceLimit { + uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; + }; + ++struct CGroupIODeviceLatency { ++ LIST_FIELDS(CGroupIODeviceLatency, device_latencies); ++ char *path; ++ usec_t target_usec; ++}; ++ + struct CGroupBlockIODeviceWeight { + LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights); + char *path; +@@ -85,6 +92,7 @@ struct CGroupContext { + uint64_t startup_io_weight; + LIST_HEAD(CGroupIODeviceWeight, io_device_weights); + LIST_HEAD(CGroupIODeviceLimit, io_device_limits); ++ LIST_HEAD(CGroupIODeviceLatency, io_device_latencies); + + uint64_t memory_low; + uint64_t memory_high; +@@ -137,6 +145,7 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c); + void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a); + void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w); + void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l); ++void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l); + void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); + void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); + +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 30d4e83932..a1d3014d61 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -140,6 +140,36 @@ static int property_get_io_device_limits( + return sd_bus_message_close_container(reply); + } + ++static int property_get_io_device_latency( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ CGroupContext *c = userdata; ++ CGroupIODeviceLatency *l; ++ int r; ++ ++ assert(bus); ++ assert(reply); ++ assert(c); ++ ++ r = sd_bus_message_open_container(reply, 'a', "(st)"); ++ if (r < 0) ++ return r; ++ ++ LIST_FOREACH(device_latencies, l, c->io_device_latencies) { ++ r = sd_bus_message_append(reply, "(st)", l->path, l->target_usec); ++ if (r < 0) ++ return r; ++ } ++ ++ return sd_bus_message_close_container(reply); ++} ++ + static int property_get_blockio_device_weight( + sd_bus *bus, + const char *path, +@@ -314,6 +344,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), ++ SD_BUS_PROPERTY("IODeviceLatencyTargetUSec", "a(st)", property_get_io_device_latency, 0, 0), + SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), + SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0), + SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0), +@@ -898,6 +929,86 @@ int bus_cgroup_set_property( + + return 1; + ++ } else if (streq(name, "IODeviceLatencyTargetUSec")) { ++ const char *path; ++ uint64_t target; ++ unsigned n = 0; ++ ++ r = sd_bus_message_enter_container(message, 'a', "(st)"); ++ if (r < 0) ++ return r; ++ ++ while ((r = sd_bus_message_read(message, "(st)", &path, &target)) > 0) { ++ ++ if (!path_is_normalized(path)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ CGroupIODeviceLatency *a = NULL, *b; ++ ++ LIST_FOREACH(device_latencies, b, c->io_device_latencies) { ++ if (path_equal(b->path, path)) { ++ a = b; ++ break; ++ } ++ } ++ ++ if (!a) { ++ a = new0(CGroupIODeviceLatency, 1); ++ if (!a) ++ return -ENOMEM; ++ ++ a->path = strdup(path); ++ if (!a->path) { ++ free(a); ++ return -ENOMEM; ++ } ++ LIST_PREPEND(device_latencies, c->io_device_latencies, a); ++ } ++ ++ a->target_usec = target; ++ } ++ ++ n++; ++ } ++ ++ r = sd_bus_message_exit_container(message); ++ if (r < 0) ++ return r; ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ _cleanup_free_ char *buf = NULL; ++ _cleanup_fclose_ FILE *f = NULL; ++ char ts[FORMAT_TIMESPAN_MAX]; ++ CGroupIODeviceLatency *a; ++ size_t size = 0; ++ ++ if (n == 0) { ++ while (c->io_device_latencies) ++ cgroup_context_free_io_device_latency(c, c->io_device_latencies); ++ } ++ ++ unit_invalidate_cgroup(u, CGROUP_MASK_IO); ++ ++ f = open_memstream(&buf, &size); ++ if (!f) ++ return -ENOMEM; ++ ++ (void) __fsetlocking(f, FSETLOCKING_BYCALLER); ++ ++ fputs("IODeviceLatencyTargetSec=\n", f); ++ LIST_FOREACH(device_latencies, a, c->io_device_latencies) ++ fprintf(f, "IODeviceLatencyTargetSec=%s %s\n", ++ a->path, format_timespan(ts, sizeof(ts), a->target_usec, 1)); ++ ++ r = fflush_and_check(f); ++ if (r < 0) ++ return r; ++ unit_write_setting(u, flags, name, buf); ++ } ++ ++ return 1; ++ + } else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + const char *path; + bool read = true; +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 8883818ff2..23879c001f 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -185,6 +185,7 @@ $1.IOReadBandwidthMax, config_parse_io_limit, 0, + $1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) + $1.IOReadIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) + $1.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) ++$1.IODeviceLatencyTargetSec, config_parse_io_device_latency, 0, offsetof($1, cgroup_context) + $1.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting) + $1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.blockio_weight) + $1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 9b2724307d..1e22013b75 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3383,6 +3383,77 @@ int config_parse_io_device_weight( + return 0; + } + ++int config_parse_io_device_latency( ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *section, ++ unsigned section_line, ++ const char *lvalue, ++ int ltype, ++ const char *rvalue, ++ void *data, ++ void *userdata) { ++ ++ _cleanup_free_ char *path = NULL, *resolved = NULL; ++ CGroupIODeviceLatency *l; ++ CGroupContext *c = data; ++ const char *p = rvalue; ++ usec_t usec; ++ int r; ++ ++ assert(filename); ++ assert(lvalue); ++ assert(rvalue); ++ ++ if (isempty(rvalue)) { ++ while (c->io_device_latencies) ++ cgroup_context_free_io_device_latency(c, c->io_device_latencies); ++ ++ return 0; ++ } ++ ++ r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES); ++ if (r == -ENOMEM) ++ return log_oom(); ++ if (r < 0) { ++ log_syntax(unit, LOG_WARNING, filename, line, r, ++ "Invalid syntax, ignoring: %s", rvalue); ++ return 0; ++ } ++ if (r == 0 || isempty(p)) { ++ log_syntax(unit, LOG_WARNING, filename, line, 0, ++ "Failed to extract device path and latency from '%s', ignoring.", rvalue); ++ return 0; ++ } ++ ++ r = unit_full_printf(userdata, path, &resolved); ++ if (r < 0) { ++ log_syntax(unit, LOG_WARNING, filename, line, r, ++ "Failed to resolve unit specifiers in '%s', ignoring: %m", path); ++ return 0; ++ } ++ ++ r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); ++ if (r < 0) ++ return 0; ++ ++ if (parse_sec(p, &usec) < 0) { ++ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", p); ++ return 0; ++ } ++ ++ l = new0(CGroupIODeviceLatency, 1); ++ if (!l) ++ return log_oom(); ++ ++ l->path = TAKE_PTR(resolved); ++ l->target_usec = usec; ++ ++ LIST_PREPEND(device_latencies, c->io_device_latencies, l); ++ return 0; ++} ++ + int config_parse_io_limit( + const char *unit, + const char *filename, +@@ -4572,6 +4643,7 @@ void unit_dump_config_items(FILE *f) { + { config_parse_device_policy, "POLICY" }, + { config_parse_io_limit, "LIMIT" }, + { config_parse_io_device_weight, "DEVICEWEIGHT" }, ++ { config_parse_io_device_latency, "DEVICELATENCY" }, + { config_parse_blockio_bandwidth, "BANDWIDTH" }, + { config_parse_blockio_weight, "WEIGHT" }, + { config_parse_blockio_device_weight, "DEVICEWEIGHT" }, +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index 424fa478a7..65a94d53cc 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -68,6 +68,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max); + CONFIG_PARSER_PROTOTYPE(config_parse_delegate); + CONFIG_PARSER_PROTOTYPE(config_parse_device_policy); + CONFIG_PARSER_PROTOTYPE(config_parse_device_allow); ++CONFIG_PARSER_PROTOTYPE(config_parse_io_device_latency); + CONFIG_PARSER_PROTOTYPE(config_parse_io_device_weight); + CONFIG_PARSER_PROTOTYPE(config_parse_io_limit); + CONFIG_PARSER_PROTOTYPE(config_parse_blockio_weight); +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index e0b2cfb170..3c1ecf2027 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -566,6 +566,37 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons + return 1; + } + ++ if (streq(field, "IODeviceLatencyTargetSec")) { ++ const char *field_usec = "IODeviceLatencyTargetUSec"; ++ ++ if (isempty(eq)) ++ r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY); ++ else { ++ const char *path, *target, *e; ++ usec_t usec; ++ ++ e = strchr(eq, ' '); ++ if (!e) { ++ log_error("Failed to parse %s value %s.", field, eq); ++ return -EINVAL; ++ } ++ ++ path = strndupa(eq, e - eq); ++ target = e+1; ++ ++ r = parse_sec(target, &usec); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse %s value %s: %m", field, target); ++ ++ r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec); ++ } ++ ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ return 1; ++ } ++ + if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) { + unsigned char prefixlen; + union in_addr_union prefix = {}; +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index a3074bc5e3..559e49f104 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -4875,6 +4875,28 @@ static int print_property(const char *name, sd_bus_message *m, bool value, bool + + return 1; + ++ } else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && ++ streq(name, "IODeviceLatencyTargetUSec")) { ++ char ts[FORMAT_TIMESPAN_MAX]; ++ const char *path; ++ uint64_t target; ++ ++ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)"); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ while ((r = sd_bus_message_read(m, "(st)", &path, &target)) > 0) ++ print_prop(name, "%s %s", strna(path), ++ format_timespan(ts, sizeof(ts), target, 1)); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ r = sd_bus_message_exit_container(m); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ return 1; ++ + } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "StandardInputData")) { + _cleanup_free_ char *h = NULL; + const void *p; diff --git a/SOURCES/0396-time-util-Introduce-parse_sec_def_infinity.patch b/SOURCES/0396-time-util-Introduce-parse_sec_def_infinity.patch new file mode 100644 index 0000000..8af140b --- /dev/null +++ b/SOURCES/0396-time-util-Introduce-parse_sec_def_infinity.patch @@ -0,0 +1,122 @@ +From 9692477a59c47b5fb6bd6d4702302859296db070 Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Wed, 23 Jan 2019 19:48:54 -0800 +Subject: [PATCH] time-util: Introduce parse_sec_def_infinity + +This works like parse_sec() but defaults to USEC_INFINITY when passed an +empty string or only whitespace. + +Also introduce config_parse_sec_def_infinity, which can be used to parse +config options using this function. + +This is useful for time options that use "infinity" for default and that +can be reset by unsetting them. + +Introduce a test case to ensure it works as expected. + +(cherry picked from commit 7b61ce3c44ef5908e817009ce4f9d2a7a37722be) + +Related: #1770379 +--- + src/basic/time-util.c | 9 +++++++++ + src/basic/time-util.h | 1 + + src/shared/conf-parser.c | 1 + + src/shared/conf-parser.h | 1 + + src/test/test-time-util.c | 21 +++++++++++++++++++++ + 5 files changed, 33 insertions(+) + +diff --git a/src/basic/time-util.c b/src/basic/time-util.c +index fe201c398d..c36e462193 100644 +--- a/src/basic/time-util.c ++++ b/src/basic/time-util.c +@@ -1072,6 +1072,15 @@ int parse_sec_fix_0(const char *t, usec_t *usec) { + return parse_sec(t, usec); + } + ++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); ++} ++ + int parse_nsec(const char *t, nsec_t *nsec) { + static const struct { + const char *suffix; +diff --git a/src/basic/time-util.h b/src/basic/time-util.h +index 344f2dc52e..f5c9ea6327 100644 +--- a/src/basic/time-util.h ++++ b/src/basic/time-util.h +@@ -116,6 +116,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/shared/conf-parser.c b/src/shared/conf-parser.c +index 2d62fdf05d..246b7431e4 100644 +--- a/src/shared/conf-parser.c ++++ b/src/shared/conf-parser.c +@@ -509,6 +509,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 16f042d894..a0a5c89c27 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/test/test-time-util.c b/src/test/test-time-util.c +index 87de8d172c..354a01dd1a 100644 +--- a/src/test/test-time-util.c ++++ b/src/test/test-time-util.c +@@ -61,6 +61,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; + +@@ -420,6 +440,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); diff --git a/SOURCES/0397-cgroup-use-structured-initialization.patch b/SOURCES/0397-cgroup-use-structured-initialization.patch new file mode 100644 index 0000000..7b9e695 --- /dev/null +++ b/SOURCES/0397-cgroup-use-structured-initialization.patch @@ -0,0 +1,63 @@ +From f7b462bacb3c0ed1f7bbe63193e9e349aafd21d3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 20 Nov 2018 19:45:02 +0100 +Subject: [PATCH] cgroup: use structured initialization + +(cherry picked from commit de8a711a5849f9239c93aefa5554a62986dfce42) + +Related: #1770379 +--- + src/core/cgroup.c | 33 +++++++++++++++++---------------- + 1 file changed, 17 insertions(+), 16 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index ad8219bd79..7aa7db9261 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -71,29 +71,30 @@ static void cgroup_compat_warn(void) { + void cgroup_context_init(CGroupContext *c) { + assert(c); + +- /* Initialize everything to the kernel defaults, assuming the +- * structure is preinitialized to 0 */ ++ /* Initialize everything to the kernel defaults. */ + +- c->cpu_weight = CGROUP_WEIGHT_INVALID; +- c->startup_cpu_weight = CGROUP_WEIGHT_INVALID; +- c->cpu_quota_per_sec_usec = USEC_INFINITY; ++ *c = (CGroupContext) { ++ .cpu_weight = CGROUP_WEIGHT_INVALID, ++ .startup_cpu_weight = CGROUP_WEIGHT_INVALID, ++ .cpu_quota_per_sec_usec = USEC_INFINITY, + +- c->cpu_shares = CGROUP_CPU_SHARES_INVALID; +- c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID; ++ .cpu_shares = CGROUP_CPU_SHARES_INVALID, ++ .startup_cpu_shares = CGROUP_CPU_SHARES_INVALID, + +- c->memory_high = CGROUP_LIMIT_MAX; +- c->memory_max = CGROUP_LIMIT_MAX; +- c->memory_swap_max = CGROUP_LIMIT_MAX; ++ .memory_high = CGROUP_LIMIT_MAX, ++ .memory_max = CGROUP_LIMIT_MAX, ++ .memory_swap_max = CGROUP_LIMIT_MAX, + +- c->memory_limit = CGROUP_LIMIT_MAX; ++ .memory_limit = CGROUP_LIMIT_MAX, + +- c->io_weight = CGROUP_WEIGHT_INVALID; +- c->startup_io_weight = CGROUP_WEIGHT_INVALID; ++ .io_weight = CGROUP_WEIGHT_INVALID, ++ .startup_io_weight = CGROUP_WEIGHT_INVALID, + +- c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; +- c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; ++ .blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID, ++ .startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID, + +- c->tasks_max = (uint64_t) -1; ++ .tasks_max = CGROUP_LIMIT_MAX, ++ }; + } + + void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { diff --git a/SOURCES/0398-core-add-CPUQuotaPeriodSec.patch b/SOURCES/0398-core-add-CPUQuotaPeriodSec.patch new file mode 100644 index 0000000..b411152 --- /dev/null +++ b/SOURCES/0398-core-add-CPUQuotaPeriodSec.patch @@ -0,0 +1,403 @@ +From 11df25536319a4c0f3276c2218054243d9ee213c Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Fri, 2 Nov 2018 09:21:57 -0700 +Subject: [PATCH] core: add CPUQuotaPeriodSec= + +This new setting allows configuration of CFS period on the CPU cgroup, instead +of using a hardcoded default of 100ms. + +Tested: +- Legacy cgroup + Unified cgroup +- systemctl set-property +- systemctl show +- Confirmed that the cgroup settings (such as cpu.cfs_period_ns) were set + appropriately, including updating the CPU quota (cpu.cfs_quota_ns) when + CPUQuotaPeriodSec= is updated. +- Checked that clamping works properly when either period or (quota * period) + are below the resolution of 1ms, or if period is above the max of 1s. + +(cherry picked from commit 10f28641115733c61754342d5dcbe70b083bea4b) + +Resolves: #1770379 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.resource-control.xml | 19 +++++++++ + src/core/cgroup.c | 61 ++++++++++++++++++++++----- + src/core/cgroup.h | 3 ++ + src/core/dbus-cgroup.c | 23 ++++++++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/load-fragment.c | 1 + + src/shared/bus-unit-util.c | 14 ++++++ + src/test/meson.build | 5 +++ + src/test/test-cgroup-cpu.c | 38 +++++++++++++++++ + 10 files changed, 155 insertions(+), 11 deletions(-) + create mode 100644 src/test/test-cgroup-cpu.c + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 23fe84e4d1..0d2d3e9065 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -218,6 +218,7 @@ All cgroup/resource control settings are available for transient units + ✓ CPUShares= + ✓ StartupCPUShares= + ✓ CPUQuota= ++✓ CPUQuotaPeriodSec= + ✓ AllowedCPUs= + ✓ AllowedMemoryNodes= + ✓ MemoryAccounting= +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index b0064bf98f..cfe19a6574 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -231,6 +231,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= + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 7aa7db9261..45fd64a394 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -23,7 +23,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) + + bool manager_owns_root_cgroup(Manager *m) { + assert(m); +@@ -77,6 +77,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, +@@ -190,6 +191,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); +@@ -211,6 +213,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + "%sCPUShares=%" PRIu64 "\n" + "%sStartupCPUShares=%" PRIu64 "\n" + "%sCPUQuotaPerSecSec=%s\n" ++ "%sCPUQuotaPeriodSec=%s\n" + "%sAllowedCPUs=%s\n" + "%sAllowedMemoryNodes=%s\n" + "%sIOWeight=%" PRIu64 "\n" +@@ -236,6 +239,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, cpuset_cpus, + prefix, cpuset_mems, + prefix, c->io_weight, +@@ -515,7 +519,40 @@ static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state) + return CGROUP_CPU_SHARES_DEFAULT; + } + +-static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota) { ++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, LOG_WARNING, 0, ++ "Clamping CPU interval for cpu.max: period is now %s", ++ format_timespan(v, sizeof(v), new_period, 1)); ++ } ++ ++ return new_period; ++} ++ ++static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota, usec_t period) { + char buf[MAX(DECIMAL_STR_MAX(uint64_t) + 1, (DECIMAL_STR_MAX(usec_t) + 1) * 2)]; + int r; + +@@ -525,11 +562,12 @@ static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t q + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.weight: %m"); + ++ 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); + + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.max", buf); + +@@ -538,7 +576,7 @@ static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t q + "Failed to set cpu.max: %m"); + } + +-static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota) { ++static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota, usec_t period) { + char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1]; + int r; + +@@ -548,20 +586,21 @@ static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t qu + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.shares: %m"); + +- 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); + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_period_us", buf); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_period_us: %m"); + + if (quota != USEC_INFINITY) { +- xsprintf(buf, USEC_FMT "\n", quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC); +- r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf); +- } else ++ xsprintf(buf, USEC_FMT "\n", MAX(quota * period / USEC_PER_SEC, USEC_PER_MSEC)); + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1"); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_quota_us: %m"); ++ } + } + + static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) { +@@ -815,7 +854,7 @@ static void cgroup_context_apply( + } else + weight = CGROUP_WEIGHT_DEFAULT; + +- cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec); ++ cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec, c->cpu_quota_period_usec); + } else { + uint64_t shares; + +@@ -831,7 +870,7 @@ static void cgroup_context_apply( + else + shares = CGROUP_CPU_SHARES_DEFAULT; + +- cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec); ++ cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec, c->cpu_quota_period_usec); + } + } + +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index f7365b4c46..2ba57d3ded 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -84,6 +84,7 @@ struct CGroupContext { + uint64_t cpu_weight; + uint64_t startup_cpu_weight; + usec_t cpu_quota_per_sec_usec; ++ usec_t cpu_quota_period_usec; + + CPUSet cpuset_cpus; + CPUSet cpuset_mems; +@@ -136,6 +137,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/dbus-cgroup.c b/src/core/dbus-cgroup.c +index a1d3014d61..c8b918e45d 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -334,6 +334,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("AllowedCPUs", "ay", property_get_cpuset, offsetof(CGroupContext, cpuset_cpus), 0), + SD_BUS_PROPERTY("AllowedMemoryNodes", "ay", property_get_cpuset, offsetof(CGroupContext, cpuset_mems), 0), + SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0), +@@ -725,6 +726,28 @@ 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; ++ 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 (STR_IN_SET(name, "AllowedCPUs", "AllowedMemoryNodes")) { + const void *a; + size_t n; +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 23879c001f..4defa82ac1 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -169,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.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 1e22013b75..762b106007 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -55,6 +55,7 @@ + #include "unit-name.h" + #include "unit-printf.h" + #include "user-util.h" ++#include "time-util.h" + #include "web-util.h" + + static int supported_socket_protocol_from_string(const char *s) { +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 3c1ecf2027..ec45d6f86d 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -480,6 +480,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)) +diff --git a/src/test/meson.build b/src/test/meson.build +index ead000e30c..22264d034c 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -513,6 +513,11 @@ tests += [ + [], + '', 'manual'], + ++ [['src/test/test-cgroup-cpu.c'], ++ [libcore, ++ libshared], ++ []], ++ + [['src/test/test-cgroup-mask.c', + 'src/test/test-helper.c'], + [libcore, +diff --git a/src/test/test-cgroup-cpu.c b/src/test/test-cgroup-cpu.c +new file mode 100644 +index 0000000000..a445acc955 +--- /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/SOURCES/0399-core-downgrade-CPUQuotaPeriodSec-clamping-logs-to-de.patch b/SOURCES/0399-core-downgrade-CPUQuotaPeriodSec-clamping-logs-to-de.patch new file mode 100644 index 0000000..e584cb5 --- /dev/null +++ b/SOURCES/0399-core-downgrade-CPUQuotaPeriodSec-clamping-logs-to-de.patch @@ -0,0 +1,66 @@ +From 9d58db234dc9f22a4f551b1b06bd9fc60d085f4d Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Wed, 23 Jan 2019 20:19:44 -0800 +Subject: [PATCH] core: downgrade CPUQuotaPeriodSec= clamping logs to debug + +After the first warning log, further messages are downgraded to LOG_DEBUG. + +(cherry picked from commit 527ede0c638b47b62a87900438a8a09dea42889e) + +Related: #1770379 +--- + src/core/cgroup.c | 3 ++- + src/core/dbus-cgroup.c | 2 ++ + src/core/unit.h | 3 +++ + 3 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 45fd64a394..f8b351a65d 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -544,9 +544,10 @@ static usec_t cgroup_cpu_adjust_period_and_log(Unit *u, usec_t period, usec_t qu + + if (new_period != period) { + char v[FORMAT_TIMESPAN_MAX]; +- log_unit_full(u, LOG_WARNING, 0, ++ 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; +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index c8b918e45d..4555b33b1f 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -712,6 +712,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) +@@ -735,6 +736,7 @@ int bus_cgroup_set_property( + + 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="); +diff --git a/src/core/unit.h b/src/core/unit.h +index 39179f5fd4..b40ff9b961 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -356,6 +356,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. */ + int last_section_private:2; diff --git a/SOURCES/0400-sd-bus-avoid-magic-number-in-SASL-length-calculation.patch b/SOURCES/0400-sd-bus-avoid-magic-number-in-SASL-length-calculation.patch new file mode 100644 index 0000000..f0bf8b7 --- /dev/null +++ b/SOURCES/0400-sd-bus-avoid-magic-number-in-SASL-length-calculation.patch @@ -0,0 +1,41 @@ +From d524f9f9089b71d83e991ed42d25a4616ec575a2 Mon Sep 17 00:00:00 2001 +From: David Rheinsberg +Date: Thu, 14 Mar 2019 13:26:50 +0100 +Subject: [PATCH] sd-bus: avoid magic number in SASL length calculation + +Lets avoid magic numbers and use a constant `strlen()` instead. + +Signed-off-by: David Rheinsberg +(cherry picked from commit 3cacdab925c40a5d9b7cf3f67719201bbaa17f67) + +Related: #1838081 +--- + src/libsystemd/sd-bus/bus-socket.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c +index 4a72795d2b..1c8b331b48 100644 +--- a/src/libsystemd/sd-bus/bus-socket.c ++++ b/src/libsystemd/sd-bus/bus-socket.c +@@ -390,7 +390,9 @@ 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) +@@ -402,7 +404,9 @@ static int bus_socket_auth_verify_server(sd_bus *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) diff --git a/SOURCES/0401-sd-bus-fix-SASL-reply-to-empty-AUTH.patch b/SOURCES/0401-sd-bus-fix-SASL-reply-to-empty-AUTH.patch new file mode 100644 index 0000000..636fad0 --- /dev/null +++ b/SOURCES/0401-sd-bus-fix-SASL-reply-to-empty-AUTH.patch @@ -0,0 +1,55 @@ +From badb16c481cf592a1761ad20dd0a84614d2bbd5b Mon Sep 17 00:00:00 2001 +From: David Rheinsberg +Date: Thu, 14 Mar 2019 13:33:28 +0100 +Subject: [PATCH] sd-bus: fix SASL reply to empty AUTH + +The correct way to reply to "AUTH " without any payload is to +send "DATA" rather than "OK". The "DATA" reply triggers the client to +respond with the requested payload. + +In fact, adding the data as hex-encoded argument like +"AUTH " is an optimization that skips the "DATA" +roundtrip. The standard way to perform an authentication is to send the +"DATA" line. + +This commit fixes sd-bus to properly send the "DATA" line. Surprisingly +no existing implementation depends on this, as they all pass the data +directly as argument to "AUTH". This will not work if we want to pass +an empty argument, though. + +Signed-off-by: David Rheinsberg +(cherry picked from commit 2010873b4b49b223e0cc07d28205b09c693ef005) + +Related: #1838081 +--- + src/libsystemd/sd-bus/bus-socket.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c +index 1c8b331b48..e505d43c6b 100644 +--- a/src/libsystemd/sd-bus/bus-socket.c ++++ b/src/libsystemd/sd-bus/bus-socket.c +@@ -399,7 +399,10 @@ static int bus_socket_auth_verify_server(sd_bus *b) { + 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")) { +@@ -413,7 +416,10 @@ static int bus_socket_auth_verify_server(sd_bus *b) { + 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")) diff --git a/SOURCES/0402-sd-bus-skip-sending-formatted-UIDs-via-SASL.patch b/SOURCES/0402-sd-bus-skip-sending-formatted-UIDs-via-SASL.patch new file mode 100644 index 0000000..063be89 --- /dev/null +++ b/SOURCES/0402-sd-bus-skip-sending-formatted-UIDs-via-SASL.patch @@ -0,0 +1,210 @@ +From ccdb8883c6ff0e72d5e221768af0718e25cbf177 Mon Sep 17 00:00:00 2001 +From: David Rheinsberg +Date: Thu, 14 Mar 2019 13:34:13 +0100 +Subject: [PATCH] sd-bus: skip sending formatted UIDs via SASL + +The dbus external authentication takes as optional argument the UID the +sender wants to authenticate as. This uid is purely optional. The +AF_UNIX socket already conveys the same information through the +auxiliary socket data, so we really don't have to provide that +information. + +Unfortunately, there is no way to send empty arguments, since they are +interpreted as "missing argument", which has a different meaning. The +SASL negotiation thus changes from: + + AUTH EXTERNAL + NEGOTIATE_UNIX_FD (optional) + BEGIN + +to: + + AUTH EXTERNAL + DATA + NEGOTIATE_UNIX_FD (optional) + BEGIN + +And thus the replies we expect as a client change from: + + OK + AGREE_UNIX_FD (optional) + +to: + + DATA + OK + AGREE_UNIX_FD (optional) + +Since the old sd-bus server implementation used the wrong reply for +"AUTH" requests that do not carry the arguments inlined, we decided to +make sd-bus clients accept this as well. Hence, sd-bus now allows +"OK \r\n" replies instead of "DATA\r\n" replies. + +Signed-off-by: David Rheinsberg +(cherry picked from commit 1ed4723d38cd0d1423c8fe650f90fa86007ddf55) + +Resolves: #1838081 +--- + src/libsystemd/sd-bus/bus-socket.c | 106 +++++++++++++++++------------ + 1 file changed, 64 insertions(+), 42 deletions(-) + +diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c +index e505d43c6b..8813dd5efd 100644 +--- a/src/libsystemd/sd-bus/bus-socket.c ++++ b/src/libsystemd/sd-bus/bus-socket.c +@@ -162,17 +162,25 @@ 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; + +@@ -187,13 +195,30 @@ 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 (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; ++ } ++ ++ /* Now check the OK line. */ + +- if (e - (char*) b->rbuffer != 3 + 32) ++ if (e - d != 2 + 3 + 32) + return -EPERM; + +- if (memcmp(b->rbuffer, "OK ", 3)) ++ if (memcmp(d + 2, "OK ", 3)) + return -EPERM; + + b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL; +@@ -201,8 +226,8 @@ static int bus_socket_auth_verify_client(sd_bus *b) { + for (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; +@@ -216,7 +241,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 = +@@ -616,42 +641,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[0].iov_base = (void*) auth_prefix; +- b->auth_iovec[0].iov_len = 1 + strlen(auth_prefix + 1); +- b->auth_iovec[1].iov_base = (void*) b->auth_buffer; +- b->auth_iovec[1].iov_len = l * 2; +- b->auth_iovec[2].iov_base = (void*) auth_suffix; +- b->auth_iovec[2].iov_len = strlen(auth_suffix); ++ b->auth_iovec[i++] = IOVEC_MAKE_STRING(sasl_negotiate_unix_fd); ++ ++ b->auth_iovec[i++] = IOVEC_MAKE_STRING(sasl_begin); + + return bus_socket_write_auth(b); + } diff --git a/SOURCES/0403-core-add-MemoryMin.patch b/SOURCES/0403-core-add-MemoryMin.patch new file mode 100644 index 0000000..eadeaed --- /dev/null +++ b/SOURCES/0403-core-add-MemoryMin.patch @@ -0,0 +1,231 @@ +From 04f21709ff081a5f1a2b5ca746582a9c5c03db7f Mon Sep 17 00:00:00 2001 +From: Tejun Heo +Date: Fri, 8 Jun 2018 17:33:14 -0700 +Subject: [PATCH] core: add MemoryMin + +The kernel added support for a new cgroup memory controller knob memory.min in +bf8d5d52ffe8 ("memcg: introduce memory.min") which was merged during v4.18 +merge window. + +Add MemoryMin to support memory.min. + +(cherry picked from commit 484226357789991de0b3363beb69258be06b4c92) + +Resolves: #1763435 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.resource-control.xml | 21 +++++++++++++++++++++ + src/core/cgroup.c | 5 ++++- + src/core/cgroup.h | 1 + + src/core/dbus-cgroup.c | 7 +++++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/load-fragment.c | 4 +++- + src/shared/bus-unit-util.c | 2 +- + src/systemctl/systemctl.c | 11 +++++++++-- + 9 files changed, 48 insertions(+), 5 deletions(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 0d2d3e9065..93865c0333 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -222,6 +222,7 @@ All cgroup/resource control settings are available for transient units + ✓ AllowedCPUs= + ✓ AllowedMemoryNodes= + ✓ MemoryAccounting= ++✓ MemoryMin= + ✓ MemoryLow= + ✓ MemoryHigh= + ✓ MemoryMax= +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index cfe19a6574..63a0c87565 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -265,6 +265,27 @@ + + + ++ ++ MemoryMin=bytes ++ ++ ++ Specify the memory usage protection of the executed processes in this unit. If the memory usages of ++ this unit and all its ancestors are below their minimum boundaries, this unit's memory won't be reclaimed. ++ ++ Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is ++ parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a ++ percentage value may be specified, which is taken relative to the installed physical memory on the ++ system. This controls the memory.min control group attribute. For details about this ++ control group attribute, see cgroup-v2.txt. ++ ++ Implies MemoryAccounting=true. ++ ++ This setting is supported only if the unified control group hierarchy is used and disables ++ MemoryLimit=. ++ ++ ++ + + MemoryLow=bytes + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index f8b351a65d..9d09c65453 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -220,6 +220,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + "%sStartupIOWeight=%" PRIu64 "\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" ++ "%sMemoryMin=%" PRIu64 "\n" + "%sMemoryLow=%" PRIu64 "\n" + "%sMemoryHigh=%" PRIu64 "\n" + "%sMemoryMax=%" PRIu64 "\n" +@@ -246,6 +247,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + prefix, c->startup_io_weight, + prefix, c->blockio_weight, + prefix, c->startup_blockio_weight, ++ prefix, c->memory_min, + prefix, c->memory_low, + prefix, c->memory_high, + prefix, c->memory_max, +@@ -777,7 +779,7 @@ static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint6 + } + + static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { +- return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX; ++ return c->memory_min > 0 || c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX; + } + + static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) { +@@ -1035,6 +1037,7 @@ static void cgroup_context_apply( + log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max); + } + ++ cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min); + cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low); + cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high); + cgroup_apply_unified_memory_limit(u, "memory.max", max); +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index 2ba57d3ded..5e1be87b20 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -95,6 +95,7 @@ struct CGroupContext { + LIST_HEAD(CGroupIODeviceLimit, io_device_limits); + LIST_HEAD(CGroupIODeviceLatency, io_device_latencies); + ++ uint64_t memory_min; + uint64_t memory_low; + uint64_t memory_high; + uint64_t memory_max; +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 4555b33b1f..6ce5984a02 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -353,6 +353,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), ++ SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0), + SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0), + SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0), + SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0), +@@ -661,6 +662,9 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryAccounting")) + return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error); + ++ if (streq(name, "MemoryMin")) ++ return bus_cgroup_set_memory(u, name, &c->memory_min, message, flags, error); ++ + if (streq(name, "MemoryLow")) + return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error); + +@@ -676,6 +680,9 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryLimit")) + return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error); + ++ if (streq(name, "MemoryMinScale")) ++ return bus_cgroup_set_memory_scale(u, name, &c->memory_min, message, flags, error); ++ + if (streq(name, "MemoryLowScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 4defa82ac1..1c6e539f30 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -171,6 +171,7 @@ $1.StartupCPUShares, config_parse_cpu_shares, 0, + $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) + $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 762b106007..d43b0f08f9 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3096,7 +3096,9 @@ int config_parse_memory_limit( + } + } + +- if (streq(lvalue, "MemoryLow")) ++ if (streq(lvalue, "MemoryMin")) ++ c->memory_min = bytes; ++ else if (streq(lvalue, "MemoryLow")) + c->memory_low = bytes; + else if (streq(lvalue, "MemoryHigh")) + c->memory_high = bytes; +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index ec45d6f86d..203c068d02 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -429,7 +429,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons + return 1; + } + +- if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { ++ if (STR_IN_SET(field, "MemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { + + if (isempty(eq) || streq(eq, "infinity")) { + r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 559e49f104..35ad20f510 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3905,6 +3905,7 @@ typedef struct UnitStatusInfo { + + /* CGroup */ + uint64_t memory_current; ++ uint64_t memory_min; + uint64_t memory_low; + uint64_t memory_high; + uint64_t memory_max; +@@ -4284,12 +4285,17 @@ static void print_status_info( + + printf(" Memory: %s", format_bytes(buf, sizeof(buf), i->memory_current)); + +- if (i->memory_low > 0 || i->memory_high != CGROUP_LIMIT_MAX || +- i->memory_max != CGROUP_LIMIT_MAX || i->memory_swap_max != CGROUP_LIMIT_MAX || ++ if (i->memory_min > 0 || i->memory_low > 0 || ++ i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX || ++ i->memory_swap_max != CGROUP_LIMIT_MAX || + i->memory_limit != CGROUP_LIMIT_MAX) { + const char *prefix = ""; + + printf(" ("); ++ if (i->memory_min > 0) { ++ printf("%smin: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_min)); ++ prefix = " "; ++ } + if (i->memory_low > 0) { + printf("%slow: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_low)); + prefix = " "; +@@ -5022,6 +5028,7 @@ static int show_one( + { "Where", "s", NULL, offsetof(UnitStatusInfo, where) }, + { "What", "s", NULL, offsetof(UnitStatusInfo, what) }, + { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) }, ++ { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) }, + { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) }, + { "MemoryHigh", "t", NULL, offsetof(UnitStatusInfo, memory_high) }, + { "MemoryMax", "t", NULL, offsetof(UnitStatusInfo, memory_max) }, diff --git a/SOURCES/0404-core-introduce-cgroup_add_device_allow.patch b/SOURCES/0404-core-introduce-cgroup_add_device_allow.patch new file mode 100644 index 0000000..351e10a --- /dev/null +++ b/SOURCES/0404-core-introduce-cgroup_add_device_allow.patch @@ -0,0 +1,98 @@ +From d8024b3de8ce376cdea48ffa59a44b050f215470 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 6 Aug 2018 13:42:14 +0900 +Subject: [PATCH] core: introduce cgroup_add_device_allow() + +(cherry picked from commit fd870bac25c2dd36affaed0251b5a7023f635306) + +Related: #1763435 +--- + src/core/cgroup.c | 29 +++++++++++++++++++++++++++++ + src/core/cgroup.h | 2 ++ + src/core/load-fragment.c | 13 +------------ + 3 files changed, 32 insertions(+), 12 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 9d09c65453..a17b38f914 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -341,6 +341,35 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + } + } + ++int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) { ++ _cleanup_free_ CGroupDeviceAllow *a = NULL; ++ _cleanup_free_ char *d = NULL; ++ ++ assert(c); ++ assert(dev); ++ assert(isempty(mode) || in_charset(mode, "rwm")); ++ ++ a = new(CGroupDeviceAllow, 1); ++ if (!a) ++ return -ENOMEM; ++ ++ d = strdup(dev); ++ if (!d) ++ return -ENOMEM; ++ ++ *a = (CGroupDeviceAllow) { ++ .path = TAKE_PTR(d), ++ .r = isempty(mode) || !!strchr(mode, 'r'), ++ .w = isempty(mode) || !!strchr(mode, 'w'), ++ .m = isempty(mode) || !!strchr(mode, 'm'), ++ }; ++ ++ LIST_PREPEND(device_allow, c->device_allow, a); ++ TAKE_PTR(a); ++ ++ return 0; ++} ++ + static int lookup_block_device(const char *p, dev_t *ret) { + struct stat st; + int r; +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index 5e1be87b20..8102b442b8 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -153,6 +153,8 @@ void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLaten + void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); + void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); + ++int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode); ++ + CGroupMask unit_get_own_mask(Unit *u); + CGroupMask unit_get_delegate_mask(Unit *u); + CGroupMask unit_get_members_mask(Unit *u); +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index d43b0f08f9..89a3457acc 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3250,7 +3250,6 @@ int config_parse_device_allow( + + _cleanup_free_ char *path = NULL, *resolved = NULL; + CGroupContext *c = data; +- CGroupDeviceAllow *a; + const char *p = rvalue; + int r; + +@@ -3299,17 +3298,7 @@ int config_parse_device_allow( + return 0; + } + +- a = new0(CGroupDeviceAllow, 1); +- if (!a) +- return log_oom(); +- +- a->path = TAKE_PTR(resolved); +- a->r = isempty(p) || !!strchr(p, 'r'); +- a->w = isempty(p) || !!strchr(p, 'w'); +- a->m = isempty(p) || !!strchr(p, 'm'); +- +- LIST_PREPEND(device_allow, c->device_allow, a); +- return 0; ++ return cgroup_add_device_allow(c, resolved, p); + } + + int config_parse_io_device_weight( diff --git a/SOURCES/0405-test-remove-support-for-suffix-in-get_testdata_dir.patch b/SOURCES/0405-test-remove-support-for-suffix-in-get_testdata_dir.patch new file mode 100644 index 0000000..811da17 --- /dev/null +++ b/SOURCES/0405-test-remove-support-for-suffix-in-get_testdata_dir.patch @@ -0,0 +1,302 @@ +From e73aa709bff2eb5c4649ed7c7055f29ca42f52aa Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Tue, 11 Sep 2018 23:15:09 -0700 +Subject: [PATCH] test: remove support for suffix in get_testdata_dir() + +Instead, use path_join() in callers wherever needed. + +(cherry picked from commit 55890a40c3ec0c061c04d1395a38c26313132d12) + +Related: #1763435 +--- + src/resolve/test-dns-packet.c | 5 ++++- + src/shared/tests.c | 4 +--- + src/shared/tests.h | 2 +- + src/test/test-bpf.c | 2 +- + src/test/test-cgroup-mask.c | 2 +- + src/test/test-engine.c | 2 +- + src/test/test-execute.c | 3 ++- + src/test/test-journal-importer.c | 10 ++++++++-- + src/test/test-path.c | 5 ++++- + src/test/test-sched-prio.c | 2 +- + src/test/test-umount.c | 18 ++++++++++++++---- + src/test/test-watch-pid.c | 2 +- + 12 files changed, 39 insertions(+), 18 deletions(-) + +diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c +index 905f000dc2..0dac05e7be 100644 +--- a/src/resolve/test-dns-packet.c ++++ b/src/resolve/test-dns-packet.c +@@ -12,6 +12,7 @@ + #include "macro.h" + #include "resolved-dns-packet.h" + #include "resolved-dns-rr.h" ++#include "path-util.h" + #include "string-util.h" + #include "strv.h" + #include "tests.h" +@@ -92,6 +93,7 @@ static void test_packet_from_file(const char* filename, bool canonical) { + + int main(int argc, char **argv) { + int i, N; ++ _cleanup_free_ char *pkts_glob = NULL; + _cleanup_globfree_ glob_t g = {}; + char **fnames; + +@@ -101,7 +103,8 @@ int main(int argc, char **argv) { + N = argc - 1; + fnames = argv + 1; + } else { +- assert_se(glob(get_testdata_dir("/test-resolve/*.pkts"), GLOB_NOSORT, NULL, &g) == 0); ++ pkts_glob = path_join(NULL, get_testdata_dir(), "test-resolve/*.pkts"); ++ assert_se(glob(pkts_glob, GLOB_NOSORT, NULL, &g) == 0); + N = g.gl_pathc; + fnames = g.gl_pathv; + } +diff --git a/src/shared/tests.c b/src/shared/tests.c +index c77eb00924..100b62b9b0 100644 +--- a/src/shared/tests.c ++++ b/src/shared/tests.c +@@ -37,7 +37,7 @@ bool test_is_running_from_builddir(char **exedir) { + return r; + } + +-const char* get_testdata_dir(const char *suffix) { ++const char* get_testdata_dir(void) { + const char *env; + /* convenience: caller does not need to free result */ + static char testdir[PATH_MAX]; +@@ -61,14 +61,12 @@ const char* get_testdata_dir(const char *suffix) { + /* Try relative path, according to the install-test layout */ + assert_se(snprintf(testdir, sizeof(testdir), "%s/testdata", exedir) > 0); + +- /* test this without the suffix, as it may contain a glob */ + if (access(testdir, F_OK) < 0) { + fputs("ERROR: Cannot find testdata directory, set $SYSTEMD_TEST_DATA\n", stderr); + exit(EXIT_FAILURE); + } + } + +- strncpy(testdir + strlen(testdir), suffix, sizeof(testdir) - strlen(testdir) - 1); + return testdir; + } + +diff --git a/src/shared/tests.h b/src/shared/tests.h +index 7f45c32d32..3d696d02fd 100644 +--- a/src/shared/tests.h ++++ b/src/shared/tests.h +@@ -3,5 +3,5 @@ + + char* setup_fake_runtime_dir(void); + bool test_is_running_from_builddir(char **exedir); +-const char* get_testdata_dir(const char *suffix); ++const char* get_testdata_dir(void); + void test_setup_logging(int level); +diff --git a/src/test/test-bpf.c b/src/test/test-bpf.c +index 4d89bd46d3..6f4a22a1cc 100644 +--- a/src/test/test-bpf.c ++++ b/src/test/test-bpf.c +@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { + return EXIT_TEST_SKIP; + } + +- assert_se(set_unit_path(get_testdata_dir("")) >= 0); ++ assert_se(set_unit_path(get_testdata_dir()) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p); +diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c +index 93c3f5d856..ed2d810dd6 100644 +--- a/src/test/test-cgroup-mask.c ++++ b/src/test/test-cgroup-mask.c +@@ -26,7 +26,7 @@ static int test_cgroup_mask(void) { + } + + /* Prepare the manager. */ +- assert_se(set_unit_path(get_testdata_dir("")) >= 0); ++ assert_se(set_unit_path(get_testdata_dir()) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + if (IN_SET(r, -EPERM, -EACCES)) { +diff --git a/src/test/test-engine.c b/src/test/test-engine.c +index d072a15cb1..0f3e244dc1 100644 +--- a/src/test/test-engine.c ++++ b/src/test/test-engine.c +@@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { + } + + /* prepare the test */ +- assert_se(set_unit_path(get_testdata_dir("")) >= 0); ++ assert_se(set_unit_path(get_testdata_dir()) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + if (MANAGER_SKIP_TEST(r)) { +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index 882e866ea9..294f8fe7dd 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -815,7 +815,8 @@ int main(int argc, char *argv[]) { + } + + assert_se(runtime_dir = setup_fake_runtime_dir()); +- assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0); ++ test_execute_path = path_join(NULL, get_testdata_dir(), "test-execute"); ++ assert_se(set_unit_path(test_execute_path) >= 0); + + /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test + * cases, otherwise (and if they are present in the environment), +diff --git a/src/test/test-journal-importer.c b/src/test/test-journal-importer.c +index 56bf6a1296..8f09d5ad2f 100644 +--- a/src/test/test-journal-importer.c ++++ b/src/test/test-journal-importer.c +@@ -4,8 +4,10 @@ + #include + #include + ++#include "alloc-util.h" + #include "log.h" + #include "journal-importer.h" ++#include "path-util.h" + #include "string-util.h" + #include "tests.h" + +@@ -20,9 +22,11 @@ static void assert_iovec_entry(const struct iovec *iovec, const char* content) { + + static void test_basic_parsing(void) { + _cleanup_(journal_importer_cleanup) JournalImporter imp = {}; ++ _cleanup_free_ char *journal_data_path = NULL; + int r; + +- imp.fd = open(get_testdata_dir("/journal-data/journal-1.txt"), O_RDONLY|O_CLOEXEC); ++ journal_data_path = path_join(NULL, get_testdata_dir(), "journal-data/journal-1.txt"); ++ imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC); + assert_se(imp.fd >= 0); + + do +@@ -49,9 +53,11 @@ static void test_basic_parsing(void) { + + static void test_bad_input(void) { + _cleanup_(journal_importer_cleanup) JournalImporter imp = {}; ++ _cleanup_free_ char *journal_data_path = NULL; + int r; + +- imp.fd = open(get_testdata_dir("/journal-data/journal-2.txt"), O_RDONLY|O_CLOEXEC); ++ journal_data_path = path_join(NULL, get_testdata_dir(), "journal-data/journal-2.txt"); ++ imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC); + assert_se(imp.fd >= 0); + + do +diff --git a/src/test/test-path.c b/src/test/test-path.c +index 3a1469ae02..faae142696 100644 +--- a/src/test/test-path.c ++++ b/src/test/test-path.c +@@ -12,6 +12,7 @@ + #include "macro.h" + #include "manager.h" + #include "mkdir.h" ++#include "path-util.h" + #include "rm-rf.h" + #include "string-util.h" + #include "strv.h" +@@ -247,6 +248,7 @@ int main(int argc, char *argv[]) { + }; + + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; ++ _cleanup_free_ char *test_path = NULL; + const test_function_t *test = NULL; + Manager *m = NULL; + +@@ -255,7 +257,8 @@ int main(int argc, char *argv[]) { + log_parse_environment(); + log_open(); + +- assert_se(set_unit_path(get_testdata_dir("/test-path")) >= 0); ++ test_path = path_join(NULL, get_testdata_dir(), "test-path"); ++ assert_se(set_unit_path(test_path) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + + for (test = tests; test && *test; test++) { +diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c +index c986284155..60012e47d2 100644 +--- a/src/test/test-sched-prio.c ++++ b/src/test/test-sched-prio.c +@@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { + } + + /* prepare the test */ +- assert_se(set_unit_path(get_testdata_dir("")) >= 0); ++ assert_se(set_unit_path(get_testdata_dir()) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + if (MANAGER_SKIP_TEST(r)) { +diff --git a/src/test/test-umount.c b/src/test/test-umount.c +index 770d1a73c8..c068f7a0f0 100644 +--- a/src/test/test-umount.c ++++ b/src/test/test-umount.c +@@ -1,6 +1,8 @@ + /* SPDX-License-Identifier: LGPL-2.1+ */ + ++#include "alloc-util.h" + #include "log.h" ++#include "path-util.h" + #include "string-util.h" + #include "tests.h" + #include "umount.h" +@@ -8,10 +10,14 @@ + + static void test_mount_points_list(const char *fname) { + _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); ++ _cleanup_free_ char *testdata_fname = NULL; + MountPoint *m; + + log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/self/mountinfo"); + ++ if (fname) ++ fname = testdata_fname = path_join(NULL, get_testdata_dir(), fname); ++ + LIST_HEAD_INIT(mp_list_head); + assert_se(mount_points_list_get(fname, &mp_list_head) >= 0); + +@@ -26,10 +32,14 @@ static void test_mount_points_list(const char *fname) { + + static void test_swap_list(const char *fname) { + _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); ++ _cleanup_free_ char *testdata_fname = NULL; + MountPoint *m; + + log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/swaps"); + ++ if (fname) ++ fname = testdata_fname = path_join(NULL, get_testdata_dir(), fname); ++ + LIST_HEAD_INIT(mp_list_head); + assert_se(swap_list_get(fname, &mp_list_head) >= 0); + +@@ -48,10 +58,10 @@ int main(int argc, char **argv) { + log_open(); + + test_mount_points_list(NULL); +- test_mount_points_list(get_testdata_dir("/test-umount/empty.mountinfo")); +- test_mount_points_list(get_testdata_dir("/test-umount/garbled.mountinfo")); +- test_mount_points_list(get_testdata_dir("/test-umount/rhbug-1554943.mountinfo")); ++ test_mount_points_list("/test-umount/empty.mountinfo"); ++ test_mount_points_list("/test-umount/garbled.mountinfo"); ++ test_mount_points_list("/test-umount/rhbug-1554943.mountinfo"); + + test_swap_list(NULL); +- test_swap_list(get_testdata_dir("/test-umount/example.swaps")); ++ test_swap_list("/test-umount/example.swaps"); + } +diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c +index 8c70175aed..d6e2886dde 100644 +--- a/src/test/test-watch-pid.c ++++ b/src/test/test-watch-pid.c +@@ -28,7 +28,7 @@ int main(int argc, char *argv[]) { + return EXIT_TEST_SKIP; + } + +- assert_se(set_unit_path(get_testdata_dir("")) >= 0); ++ assert_se(set_unit_path(get_testdata_dir()) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + + assert_se(manager_new(UNIT_FILE_USER, true, &m) >= 0); diff --git a/SOURCES/0406-cgroup-Implement-default-propagation-of-MemoryLow-wi.patch b/SOURCES/0406-cgroup-Implement-default-propagation-of-MemoryLow-wi.patch new file mode 100644 index 0000000..cbd2d37 --- /dev/null +++ b/SOURCES/0406-cgroup-Implement-default-propagation-of-MemoryLow-wi.patch @@ -0,0 +1,695 @@ +From f45cb6d6a2c247c7594d9965cbb745303adb61d6 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Thu, 28 Mar 2019 12:50:50 +0000 +Subject: [PATCH] cgroup: Implement default propagation of MemoryLow with + DefaultMemoryLow + +In cgroup v2 we have protection tunables -- currently MemoryLow and +MemoryMin (there will be more in future for other resources, too). The +design of these protection tunables requires not only intermediate +cgroups to propagate protections, but also the units at the leaf of that +resource's operation to accept it (by setting MemoryLow or MemoryMin). + +This makes sense from an low-level API design perspective, but it's a +good idea to also have a higher-level abstraction that can, by default, +propagate these resources to children recursively. In this patch, this +happens by having descendants set memory.low to N if their ancestor has +DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow +value. + +Any affected unit can opt out of this propagation by manually setting +`MemoryLow` to some value in its unit configuration. A unit can also +stop further propagation by setting `DefaultMemoryLow=` with no +argument. This removes further propagation in the subtree, but has no +effect on the unit itself (for that, use `MemoryLow=0`). + +Our use case in production is simplifying the configuration of machines +which heavily rely on memory protection tunables, but currently require +tweaking a huge number of unit files to make that a reality. This +directive makes that significantly less fragile, and decreases the risk +of misconfiguration. + +After this patch is merged, I will implement DefaultMemoryMin= using the +same principles. + +(cherry picked from commit c52db42b78f6fbeb7792cc4eca27e2767a48b6ca) + +Related: #1763435 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.resource-control.xml | 4 + + src/core/cgroup.c | 58 +++++++++-- + src/core/cgroup.h | 6 ++ + src/core/dbus-cgroup.c | 7 ++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/load-fragment.c | 13 ++- + src/shared/bus-unit-util.c | 2 +- + src/shared/bus-util.c | 2 +- + src/systemctl/systemctl.c | 3 + + src/test/meson.build | 6 ++ + src/test/test-cgroup-unit-default.c | 145 ++++++++++++++++++++++++++ + test/dml-discard-empty.service | 7 ++ + test/dml-discard-set-ml.service | 8 ++ + test/dml-discard.slice | 5 + + test/dml-override-empty.service | 7 ++ + test/dml-override.slice | 5 + + test/dml-passthrough-empty.service | 7 ++ + test/dml-passthrough-set-dml.service | 8 ++ + test/dml-passthrough-set-ml.service | 8 ++ + test/dml-passthrough.slice | 5 + + test/dml.slice | 5 + + test/meson.build | 10 ++ + 23 files changed, 310 insertions(+), 13 deletions(-) + create mode 100644 src/test/test-cgroup-unit-default.c + create mode 100644 test/dml-discard-empty.service + create mode 100644 test/dml-discard-set-ml.service + create mode 100644 test/dml-discard.slice + create mode 100644 test/dml-override-empty.service + create mode 100644 test/dml-override.slice + create mode 100644 test/dml-passthrough-empty.service + create mode 100644 test/dml-passthrough-set-dml.service + create mode 100644 test/dml-passthrough-set-ml.service + create mode 100644 test/dml-passthrough.slice + create mode 100644 test/dml.slice + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 93865c0333..5a8fa0727e 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -223,6 +223,7 @@ All cgroup/resource control settings are available for transient units + ✓ AllowedMemoryNodes= + ✓ MemoryAccounting= + ✓ MemoryMin= ++✓ DefaultMemoryLow= + ✓ MemoryLow= + ✓ MemoryHigh= + ✓ MemoryMax= +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index 63a0c87565..27f16001dd 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -305,6 +305,10 @@ + + This setting is supported only if the unified control group hierarchy is used and disables + MemoryLimit=. ++ ++ Units may can have their children use a default memory.low value by specifying ++ DefaultMemoryLow=, which has the same usage as MemoryLow=. This setting ++ does not affect memory.low in the unit itself. + + + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index a17b38f914..f804bf4727 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -220,6 +220,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + "%sStartupIOWeight=%" PRIu64 "\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" ++ "%sDefaultMemoryLow=%" PRIu64 "\n" + "%sMemoryMin=%" PRIu64 "\n" + "%sMemoryLow=%" PRIu64 "\n" + "%sMemoryHigh=%" PRIu64 "\n" +@@ -247,6 +248,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + prefix, c->startup_io_weight, + prefix, c->blockio_weight, + prefix, c->startup_blockio_weight, ++ prefix, c->default_memory_low, + prefix, c->memory_min, + prefix, c->memory_low, + prefix, c->memory_high, +@@ -370,6 +372,32 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) + return 0; + } + ++uint64_t unit_get_ancestor_memory_low(Unit *u) { ++ CGroupContext *c; ++ ++ /* 1. Is MemoryLow set in this unit? If so, use that. ++ * 2. Is DefaultMemoryLow set in any ancestor? If so, use that. ++ * 3. Otherwise, return CGROUP_LIMIT_MIN. */ ++ ++ assert(u); ++ ++ c = unit_get_cgroup_context(u); ++ ++ if (c->memory_low_set) ++ return c->memory_low; ++ ++ while (UNIT_ISSET(u->slice)) { ++ u = UNIT_DEREF(u->slice); ++ c = unit_get_cgroup_context(u); ++ ++ if (c->default_memory_low_set) ++ return c->default_memory_low; ++ } ++ ++ /* We've reached the root, but nobody had DefaultMemoryLow set, so set it to the kernel default. */ ++ return CGROUP_LIMIT_MIN; ++} ++ + static int lookup_block_device(const char *p, dev_t *ret) { + struct stat st; + int r; +@@ -807,8 +835,17 @@ static void cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint6 + "Failed to set blkio.throttle.write_bps_device: %m"); + } + +-static bool cgroup_context_has_unified_memory_config(CGroupContext *c) { +- return c->memory_min > 0 || c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX; ++static bool unit_has_unified_memory_config(Unit *u) { ++ CGroupContext *c; ++ ++ assert(u); ++ ++ c = unit_get_cgroup_context(u); ++ assert(c); ++ ++ return c->memory_min > 0 || unit_get_ancestor_memory_low(u) > 0 || ++ c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || ++ c->memory_swap_max != CGROUP_LIMIT_MAX; + } + + static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) { +@@ -1056,7 +1093,7 @@ static void cgroup_context_apply( + if (cg_all_unified() > 0) { + uint64_t max, swap_max = CGROUP_LIMIT_MAX; + +- if (cgroup_context_has_unified_memory_config(c)) { ++ if (unit_has_unified_memory_config(u)) { + max = c->memory_max; + swap_max = c->memory_swap_max; + } else { +@@ -1067,7 +1104,7 @@ static void cgroup_context_apply( + } + + cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min); +- cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low); ++ cgroup_apply_unified_memory_limit(u, "memory.low", unit_get_ancestor_memory_low(u)); + cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high); + cgroup_apply_unified_memory_limit(u, "memory.max", max); + cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max); +@@ -1075,7 +1112,7 @@ static void cgroup_context_apply( + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + uint64_t val; + +- if (cgroup_context_has_unified_memory_config(c)) { ++ if (unit_has_unified_memory_config(u)) { + val = c->memory_max; + log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", val); + } else +@@ -1204,8 +1241,13 @@ static void cgroup_context_apply( + cgroup_apply_firewall(u); + } + +-CGroupMask cgroup_context_get_mask(CGroupContext *c) { ++static CGroupMask unit_get_cgroup_mask(Unit *u) { + CGroupMask mask = 0; ++ CGroupContext *c; ++ ++ assert(u); ++ ++ c = unit_get_cgroup_context(u); + + /* Figure out which controllers we need */ + +@@ -1223,7 +1265,7 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { + + if (c->memory_accounting || + c->memory_limit != CGROUP_LIMIT_MAX || +- cgroup_context_has_unified_memory_config(c)) ++ unit_has_unified_memory_config(u)) + mask |= CGROUP_MASK_MEMORY; + + if (c->device_allow || +@@ -1246,7 +1288,7 @@ CGroupMask unit_get_own_mask(Unit *u) { + if (!c) + return 0; + +- return cgroup_context_get_mask(c) | unit_get_delegate_mask(u); ++ return unit_get_cgroup_mask(u) | unit_get_delegate_mask(u); + } + + CGroupMask unit_get_delegate_mask(Unit *u) { +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index 8102b442b8..a263d6a169 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -95,12 +95,16 @@ struct CGroupContext { + LIST_HEAD(CGroupIODeviceLimit, io_device_limits); + LIST_HEAD(CGroupIODeviceLatency, io_device_latencies); + ++ uint64_t default_memory_low; + uint64_t memory_min; + uint64_t memory_low; + uint64_t memory_high; + uint64_t memory_max; + uint64_t memory_swap_max; + ++ bool default_memory_low_set; ++ bool memory_low_set; ++ + LIST_HEAD(IPAddressAccessItem, ip_address_allow); + LIST_HEAD(IPAddressAccessItem, ip_address_deny); + +@@ -191,6 +195,8 @@ Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup); + Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid); + Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); + ++uint64_t unit_get_ancestor_memory_low(Unit *u); ++ + int unit_search_main_pid(Unit *u, pid_t *ret); + int unit_watch_all_pids(Unit *u); + +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 6ce5984a02..2115d43b0c 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -353,6 +353,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), ++ SD_BUS_PROPERTY("DefaultMemoryLow", "t", NULL, offsetof(CGroupContext, default_memory_low), 0), + SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0), + SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0), + SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0), +@@ -668,6 +669,9 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryLow")) + return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error); + ++ if (streq(name, "DefaultMemoryLow")) ++ return bus_cgroup_set_memory(u, name, &c->default_memory_low, message, flags, error); ++ + if (streq(name, "MemoryHigh")) + return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error); + +@@ -686,6 +690,9 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryLowScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error); + ++ if (streq(name, "DefaultMemoryLowScale")) ++ return bus_cgroup_set_memory_scale(u, name, &c->default_memory_low, message, flags, error); ++ + if (streq(name, "MemoryHighScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 1c6e539f30..43cc78fdea 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -172,6 +172,7 @@ $1.CPUQuota, config_parse_cpu_quota, 0, + $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.DefaultMemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 89a3457acc..20faed02ad 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3096,11 +3096,18 @@ int config_parse_memory_limit( + } + } + +- if (streq(lvalue, "MemoryMin")) ++ if (streq(lvalue, "DefaultMemoryLow")) { ++ c->default_memory_low_set = true; ++ if (isempty(rvalue)) ++ c->default_memory_low = CGROUP_LIMIT_MIN; ++ else ++ c->default_memory_low = bytes; ++ } else if (streq(lvalue, "MemoryMin")) + c->memory_min = bytes; +- else if (streq(lvalue, "MemoryLow")) ++ else if (streq(lvalue, "MemoryLow")) { + c->memory_low = bytes; +- else if (streq(lvalue, "MemoryHigh")) ++ c->memory_low_set = true; ++ } else if (streq(lvalue, "MemoryHigh")) + c->memory_high = bytes; + else if (streq(lvalue, "MemoryMax")) + c->memory_max = bytes; +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 203c068d02..f88730a85d 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -429,7 +429,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons + return 1; + } + +- if (STR_IN_SET(field, "MemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { ++ if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { + + if (isempty(eq) || streq(eq, "infinity")) { + r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index 5ed68429be..0ba2712deb 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -774,7 +774,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all + + print_property(name, "%s", "[not set]"); + +- else if ((STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || ++ else if ((STR_IN_SET(name, "DefaultMemoryLow", "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)) +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 35ad20f510..763ca0c6b7 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3918,6 +3918,8 @@ typedef struct UnitStatusInfo { + uint64_t ip_ingress_bytes; + uint64_t ip_egress_bytes; + ++ uint64_t default_memory_low; ++ + LIST_HEAD(ExecStatusInfo, exec); + } UnitStatusInfo; + +@@ -5028,6 +5030,7 @@ static int show_one( + { "Where", "s", NULL, offsetof(UnitStatusInfo, where) }, + { "What", "s", NULL, offsetof(UnitStatusInfo, what) }, + { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) }, ++ { "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) }, + { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) }, + { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) }, + { "MemoryHigh", "t", NULL, offsetof(UnitStatusInfo, memory_high) }, +diff --git a/src/test/meson.build b/src/test/meson.build +index 22264d034c..7b310d4ec7 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -518,6 +518,12 @@ tests += [ + libshared], + []], + ++ [['src/test/test-cgroup-unit-default.c', ++ 'src/test/test-helper.c'], ++ [libcore, ++ libshared], ++ []], ++ + [['src/test/test-cgroup-mask.c', + 'src/test/test-helper.c'], + [libcore, +diff --git a/src/test/test-cgroup-unit-default.c b/src/test/test-cgroup-unit-default.c +new file mode 100644 +index 0000000000..54f7d50c45 +--- /dev/null ++++ b/src/test/test-cgroup-unit-default.c +@@ -0,0 +1,145 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++ ++#include "cgroup.h" ++#include "manager.h" ++#include "rm-rf.h" ++#include "test-helper.h" ++#include "tests.h" ++#include "unit.h" ++ ++static int test_default_memory_low(void) { ++ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; ++ _cleanup_(manager_freep) Manager *m = NULL; ++ Unit *root, *dml, ++ *dml_passthrough, *dml_passthrough_empty, *dml_passthrough_set_dml, *dml_passthrough_set_ml, ++ *dml_override, *dml_override_empty, ++ *dml_discard, *dml_discard_empty, *dml_discard_set_ml; ++ uint64_t dml_tree_default; ++ int r; ++ ++ r = enter_cgroup_subroot(); ++ if (r == -ENOMEDIUM) ++ return EXIT_TEST_SKIP; ++ ++ assert_se(set_unit_path(get_testdata_dir()) >= 0); ++ assert_se(runtime_dir = setup_fake_runtime_dir()); ++ r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); ++ if (IN_SET(r, -EPERM, -EACCES)) { ++ log_error_errno(r, "manager_new: %m"); ++ return EXIT_TEST_SKIP; ++ } ++ ++ assert_se(r >= 0); ++ assert_se(manager_startup(m, NULL, NULL) >= 0); ++ ++ /* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this: ++ * ++ * 1. dml-passthrough.slice sets MemoryLow=100. This should not affect its children, as only ++ * DefaultMemoryLow is propagated, not MemoryLow. As such, all leaf services should end up with ++ * memory.low as 50, inherited from dml.slice, *except* for dml-passthrough-set-ml.service, which ++ * should have the value of 25, as it has MemoryLow explicitly set. ++ * ++ * ┌───────────┐ ++ * │ dml.slice │ ++ * └─────┬─────┘ ++ * MemoryLow=100 ++ * ┌───────────┴───────────┐ ++ * │ dml-passthrough.slice │ ++ * └───────────┬───────────┘ ++ * ┌───────────────────────────────────┼───────────────────────────────────┐ ++ * no new settings DefaultMemoryLow=15 MemoryLow=25 ++ * ┌───────────────┴───────────────┐ ┌────────────────┴────────────────┐ ┌───────────────┴────────────────┐ ++ * │ dml-passthrough-empty.service │ │ dml-passthrough-set-dml.service │ │ dml-passthrough-set-ml.service │ ++ * └───────────────────────────────┘ └─────────────────────────────────┘ └────────────────────────────────┘ ++ * ++ * 2. dml-override.slice sets DefaultMemoryLow=10. As such, dml-override-empty.service should also ++ * end up with a memory.low of 10. dml-override.slice should still have a memory.low of 50. ++ * ++ * ┌───────────┐ ++ * │ dml.slice │ ++ * └─────┬─────┘ ++ * DefaultMemoryLow=10 ++ * ┌─────────┴──────────┐ ++ * │ dml-override.slice │ ++ * └─────────┬──────────┘ ++ * no new settings ++ * ┌─────────────┴──────────────┐ ++ * │ dml-override-empty.service │ ++ * └────────────────────────────┘ ++ * ++ * 3. dml-discard.slice sets DefaultMemoryLow= with no rvalue. As such, ++ * dml-discard-empty.service should end up with a value of 0. ++ * dml-discard-explicit-ml.service sets MemoryLow=70, and as such should have that override the ++ * reset DefaultMemoryLow value. dml-discard.slice should still have an eventual memory.low of 50. ++ * ++ * ┌───────────┐ ++ * │ dml.slice │ ++ * └─────┬─────┘ ++ * DefaultMemoryLow= ++ * ┌─────────┴─────────┐ ++ * │ dml-discard.slice │ ++ * └─────────┬─────────┘ ++ * ┌──────────────┴───────────────┐ ++ * no new settings MemoryLow=15 ++ * ┌─────────────┴─────────────┐ ┌─────────────┴──────────────┐ ++ * │ dml-discard-empty.service │ │ dml-discard-set-ml.service │ ++ * └───────────────────────────┘ └────────────────────────────┘ ++ */ ++ assert_se(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml) >= 0); ++ ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0); ++ assert_se(UNIT_DEREF(dml_passthrough->slice) == dml); ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0); ++ assert_se(UNIT_DEREF(dml_passthrough_empty->slice) == dml_passthrough); ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0); ++ assert_se(UNIT_DEREF(dml_passthrough_set_dml->slice) == dml_passthrough); ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0); ++ assert_se(UNIT_DEREF(dml_passthrough_set_ml->slice) == dml_passthrough); ++ ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0); ++ assert_se(UNIT_DEREF(dml_override->slice) == dml); ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0); ++ assert_se(UNIT_DEREF(dml_override_empty->slice) == dml_override); ++ ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0); ++ assert_se(UNIT_DEREF(dml_discard->slice) == dml); ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0); ++ assert_se(UNIT_DEREF(dml_discard_empty->slice) == dml_discard); ++ assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0); ++ assert_se(UNIT_DEREF(dml_discard_set_ml->slice) == dml_discard); ++ ++ root = UNIT_DEREF(dml->slice); ++ assert_se(!UNIT_ISSET(root->slice)); ++ ++ assert_se(unit_get_ancestor_memory_low(root) == CGROUP_LIMIT_MIN); ++ ++ assert_se(unit_get_ancestor_memory_low(dml) == CGROUP_LIMIT_MIN); ++ dml_tree_default = unit_get_cgroup_context(dml)->default_memory_low; ++ assert_se(dml_tree_default == 50); ++ ++ assert_se(unit_get_ancestor_memory_low(dml_passthrough) == 100); ++ assert_se(unit_get_ancestor_memory_low(dml_passthrough_empty) == dml_tree_default); ++ assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_dml) == 50); ++ assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 25); ++ ++ assert_se(unit_get_ancestor_memory_low(dml_override) == dml_tree_default); ++ assert_se(unit_get_ancestor_memory_low(dml_override_empty) == 10); ++ ++ assert_se(unit_get_ancestor_memory_low(dml_discard) == dml_tree_default); ++ assert_se(unit_get_ancestor_memory_low(dml_discard_empty) == CGROUP_LIMIT_MIN); ++ assert_se(unit_get_ancestor_memory_low(dml_discard_set_ml) == 15); ++ ++ return 0; ++} ++ ++int main(int argc, char* argv[]) { ++ int rc = EXIT_SUCCESS; ++ ++ test_setup_logging(LOG_DEBUG); ++ ++ TEST_REQ_RUNNING_SYSTEMD(rc = test_default_memory_low()); ++ ++ return rc; ++} +diff --git a/test/dml-discard-empty.service b/test/dml-discard-empty.service +new file mode 100644 +index 0000000000..75228f6470 +--- /dev/null ++++ b/test/dml-discard-empty.service +@@ -0,0 +1,7 @@ ++[Unit] ++Description=DML discard empty service ++ ++[Service] ++Slice=dml-discard.slice ++Type=oneshot ++ExecStart=/bin/true +diff --git a/test/dml-discard-set-ml.service b/test/dml-discard-set-ml.service +new file mode 100644 +index 0000000000..591c99270c +--- /dev/null ++++ b/test/dml-discard-set-ml.service +@@ -0,0 +1,8 @@ ++[Unit] ++Description=DML discard set ml service ++ ++[Service] ++Slice=dml-discard.slice ++Type=oneshot ++ExecStart=/bin/true ++MemoryLow=15 +diff --git a/test/dml-discard.slice b/test/dml-discard.slice +new file mode 100644 +index 0000000000..e26d86846c +--- /dev/null ++++ b/test/dml-discard.slice +@@ -0,0 +1,5 @@ ++[Unit] ++Description=DML discard slice ++ ++[Slice] ++DefaultMemoryLow= +diff --git a/test/dml-override-empty.service b/test/dml-override-empty.service +new file mode 100644 +index 0000000000..142c98720c +--- /dev/null ++++ b/test/dml-override-empty.service +@@ -0,0 +1,7 @@ ++[Unit] ++Description=DML override empty service ++ ++[Service] ++Slice=dml-override.slice ++Type=oneshot ++ExecStart=/bin/true +diff --git a/test/dml-override.slice b/test/dml-override.slice +new file mode 100644 +index 0000000000..feb6773e39 +--- /dev/null ++++ b/test/dml-override.slice +@@ -0,0 +1,5 @@ ++[Unit] ++Description=DML override slice ++ ++[Slice] ++DefaultMemoryLow=10 +diff --git a/test/dml-passthrough-empty.service b/test/dml-passthrough-empty.service +new file mode 100644 +index 0000000000..34832de491 +--- /dev/null ++++ b/test/dml-passthrough-empty.service +@@ -0,0 +1,7 @@ ++[Unit] ++Description=DML passthrough empty service ++ ++[Service] ++Slice=dml-passthrough.slice ++Type=oneshot ++ExecStart=/bin/true +diff --git a/test/dml-passthrough-set-dml.service b/test/dml-passthrough-set-dml.service +new file mode 100644 +index 0000000000..5bdf4ed4b7 +--- /dev/null ++++ b/test/dml-passthrough-set-dml.service +@@ -0,0 +1,8 @@ ++[Unit] ++Description=DML passthrough set DML service ++ ++[Service] ++Slice=dml-passthrough.slice ++Type=oneshot ++ExecStart=/bin/true ++DefaultMemoryLow=15 +diff --git a/test/dml-passthrough-set-ml.service b/test/dml-passthrough-set-ml.service +new file mode 100644 +index 0000000000..2abd591389 +--- /dev/null ++++ b/test/dml-passthrough-set-ml.service +@@ -0,0 +1,8 @@ ++[Unit] ++Description=DML passthrough set ML service ++ ++[Service] ++Slice=dml-passthrough.slice ++Type=oneshot ++ExecStart=/bin/true ++MemoryLow=25 +diff --git a/test/dml-passthrough.slice b/test/dml-passthrough.slice +new file mode 100644 +index 0000000000..1b1a848edb +--- /dev/null ++++ b/test/dml-passthrough.slice +@@ -0,0 +1,5 @@ ++[Unit] ++Description=DML passthrough slice ++ ++[Slice] ++MemoryLow=100 +diff --git a/test/dml.slice b/test/dml.slice +new file mode 100644 +index 0000000000..84e333ef04 +--- /dev/null ++++ b/test/dml.slice +@@ -0,0 +1,5 @@ ++[Unit] ++Description=DML slice ++ ++[Slice] ++DefaultMemoryLow=50 +diff --git a/test/meson.build b/test/meson.build +index 070731c4a9..52e4fa2e3c 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -7,6 +7,16 @@ test_data_files = ''' + c.service + d.service + daughter.service ++ dml.slice ++ dml-passthrough.slice ++ dml-passthrough-empty.service ++ dml-passthrough-set-dml.service ++ dml-passthrough-set-ml.service ++ dml-override.slice ++ dml-override-empty.service ++ dml-discard.slice ++ dml-discard-empty.service ++ dml-discard-set-ml.service + e.service + end.service + f.service diff --git a/SOURCES/0407-cgroup-Create-UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP.patch b/SOURCES/0407-cgroup-Create-UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP.patch new file mode 100644 index 0000000..5c43842 --- /dev/null +++ b/SOURCES/0407-cgroup-Create-UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP.patch @@ -0,0 +1,80 @@ +From a016ef4ab29ed62da547db008866624f75ed6407 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Tue, 16 Apr 2019 18:14:09 +0100 +Subject: [PATCH] cgroup: Create UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP + +This is in preparation for creating unit_get_ancestor_memory_min. + +(cherry picked from commit 6264b85e92aeddb74b8d8808a08c9eae8390a6a5) + +Related: #1763435 +--- + src/core/cgroup.c | 55 ++++++++++++++++++++++++++--------------------- + 1 file changed, 30 insertions(+), 25 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index f804bf4727..46a89ff5e1 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -372,31 +372,36 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) + return 0; + } + +-uint64_t unit_get_ancestor_memory_low(Unit *u) { +- CGroupContext *c; +- +- /* 1. Is MemoryLow set in this unit? If so, use that. +- * 2. Is DefaultMemoryLow set in any ancestor? If so, use that. +- * 3. Otherwise, return CGROUP_LIMIT_MIN. */ +- +- assert(u); +- +- c = unit_get_cgroup_context(u); +- +- if (c->memory_low_set) +- return c->memory_low; +- +- while (UNIT_ISSET(u->slice)) { +- u = UNIT_DEREF(u->slice); +- c = unit_get_cgroup_context(u); +- +- if (c->default_memory_low_set) +- return c->default_memory_low; +- } +- +- /* We've reached the root, but nobody had DefaultMemoryLow set, so set it to the kernel default. */ +- return CGROUP_LIMIT_MIN; +-} ++#define UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(entry) \ ++ uint64_t unit_get_ancestor_##entry(Unit *u) { \ ++ CGroupContext *c; \ ++ \ ++ /* 1. Is entry set in this unit? If so, use that. \ ++ * 2. Is the default for this entry set in any \ ++ * ancestor? If so, use that. \ ++ * 3. Otherwise, return CGROUP_LIMIT_MIN. */ \ ++ \ ++ assert(u); \ ++ \ ++ c = unit_get_cgroup_context(u); \ ++ \ ++ if (c->entry##_set) \ ++ return c->entry; \ ++ \ ++ while (UNIT_ISSET(u->slice)) { \ ++ u = UNIT_DEREF(u->slice); \ ++ c = unit_get_cgroup_context(u); \ ++ \ ++ if (c->default_##entry##_set) \ ++ return c->default_##entry; \ ++ } \ ++ \ ++ /* We've reached the root, but nobody had default for \ ++ * this entry set, so set it to the kernel default. */ \ ++ return CGROUP_LIMIT_MIN; \ ++} ++ ++UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low); + + static int lookup_block_device(const char *p, dev_t *ret) { + struct stat st; diff --git a/SOURCES/0408-unit-Add-DefaultMemoryMin.patch b/SOURCES/0408-unit-Add-DefaultMemoryMin.patch new file mode 100644 index 0000000..bdbac28 --- /dev/null +++ b/SOURCES/0408-unit-Add-DefaultMemoryMin.patch @@ -0,0 +1,141 @@ +From 69dbbc29f26569fd09f0109e6fbebde98c0c8567 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Tue, 16 Apr 2019 18:44:05 +0100 +Subject: [PATCH] unit: Add DefaultMemoryMin + +(cherry picked from commit 7ad5439e0663e39e36619957fa37eefe8026bcab) + +Related: #1763435 +--- + src/core/cgroup.c | 3 +++ + src/core/cgroup.h | 4 ++++ + src/core/dbus-cgroup.c | 6 ++++++ + src/core/load-fragment.c | 11 +++++++++-- + src/systemctl/systemctl.c | 2 ++ + 5 files changed, 24 insertions(+), 2 deletions(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 46a89ff5e1..d40f9cbc2a 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -220,6 +220,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + "%sStartupIOWeight=%" PRIu64 "\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" ++ "%sDefaultMemoryMin=%" PRIu64 "\n" + "%sDefaultMemoryLow=%" PRIu64 "\n" + "%sMemoryMin=%" PRIu64 "\n" + "%sMemoryLow=%" PRIu64 "\n" +@@ -248,6 +249,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + prefix, c->startup_io_weight, + prefix, c->blockio_weight, + prefix, c->startup_blockio_weight, ++ prefix, c->default_memory_min, + prefix, c->default_memory_low, + prefix, c->memory_min, + prefix, c->memory_low, +@@ -402,6 +404,7 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) + } + + UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low); ++UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_min); + + static int lookup_block_device(const char *p, dev_t *ret) { + struct stat st; +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index a263d6a169..976224336d 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -95,6 +95,7 @@ struct CGroupContext { + LIST_HEAD(CGroupIODeviceLimit, io_device_limits); + LIST_HEAD(CGroupIODeviceLatency, io_device_latencies); + ++ uint64_t default_memory_min; + uint64_t default_memory_low; + uint64_t memory_min; + uint64_t memory_low; +@@ -102,7 +103,9 @@ struct CGroupContext { + uint64_t memory_max; + uint64_t memory_swap_max; + ++ bool default_memory_min_set; + bool default_memory_low_set; ++ bool memory_min_set; + bool memory_low_set; + + LIST_HEAD(IPAddressAccessItem, ip_address_allow); +@@ -195,6 +198,7 @@ Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup); + Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid); + Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); + ++uint64_t unit_get_ancestor_memory_min(Unit *u); + uint64_t unit_get_ancestor_memory_low(Unit *u); + + int unit_search_main_pid(Unit *u, pid_t *ret); +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 2115d43b0c..e1278c317a 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -669,6 +669,9 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryLow")) + return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error); + ++ if (streq(name, "DefaultMemoryMin")) ++ return bus_cgroup_set_memory(u, name, &c->default_memory_min, message, flags, error); ++ + if (streq(name, "DefaultMemoryLow")) + return bus_cgroup_set_memory(u, name, &c->default_memory_low, message, flags, error); + +@@ -690,6 +693,9 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryLowScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error); + ++ if (streq(name, "DefaultMemoryMinScale")) ++ return bus_cgroup_set_memory_scale(u, name, &c->default_memory_min, message, flags, error); ++ + if (streq(name, "DefaultMemoryLowScale")) + return bus_cgroup_set_memory_scale(u, name, &c->default_memory_low, message, flags, error); + +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 20faed02ad..3b8ee6b124 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3102,9 +3102,16 @@ int config_parse_memory_limit( + c->default_memory_low = CGROUP_LIMIT_MIN; + else + c->default_memory_low = bytes; +- } else if (streq(lvalue, "MemoryMin")) ++ } else if (streq(lvalue, "DefaultMemoryMin")) { ++ c->default_memory_min_set = true; ++ if (isempty(rvalue)) ++ c->default_memory_min = CGROUP_LIMIT_MIN; ++ else ++ c->default_memory_min = bytes; ++ } else if (streq(lvalue, "MemoryMin")) { + c->memory_min = bytes; +- else if (streq(lvalue, "MemoryLow")) { ++ c->memory_min_set = true; ++ } else if (streq(lvalue, "MemoryLow")) { + c->memory_low = bytes; + c->memory_low_set = true; + } else if (streq(lvalue, "MemoryHigh")) +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 763ca0c6b7..e0db97e339 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3918,6 +3918,7 @@ typedef struct UnitStatusInfo { + uint64_t ip_ingress_bytes; + uint64_t ip_egress_bytes; + ++ uint64_t default_memory_min; + uint64_t default_memory_low; + + LIST_HEAD(ExecStatusInfo, exec); +@@ -5030,6 +5031,7 @@ static int show_one( + { "Where", "s", NULL, offsetof(UnitStatusInfo, where) }, + { "What", "s", NULL, offsetof(UnitStatusInfo, what) }, + { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) }, ++ { "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) }, + { "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) }, + { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) }, + { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) }, diff --git a/SOURCES/0409-cgroup-Polish-hierarchically-aware-protection-docs-a.patch b/SOURCES/0409-cgroup-Polish-hierarchically-aware-protection-docs-a.patch new file mode 100644 index 0000000..d2399a0 --- /dev/null +++ b/SOURCES/0409-cgroup-Polish-hierarchically-aware-protection-docs-a.patch @@ -0,0 +1,56 @@ +From 556375ae1d8d0b460d691888f2fb7ea520fe9a6b Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Tue, 30 Apr 2019 14:22:04 -0400 +Subject: [PATCH] cgroup: Polish hierarchically aware protection docs a bit + +I missed adding a section in `systemd.resource-control` about +DefaultMemoryMin in #12332. + +Also, add a NEWS entry going over the general concept. + +(cherry picked from commit acdb4b5236f38bbefbcc4a47fdbb9cd558b4b5c5) + +Related: #1763435 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.resource-control.xml | 8 ++++++-- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 5a8fa0727e..1a4e79190a 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -222,6 +222,7 @@ All cgroup/resource control settings are available for transient units + ✓ AllowedCPUs= + ✓ AllowedMemoryNodes= + ✓ MemoryAccounting= ++✓ DefaultMemoryMin= + ✓ MemoryMin= + ✓ DefaultMemoryLow= + ✓ MemoryLow= +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index 27f16001dd..d3bff29169 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -283,6 +283,10 @@ + + This setting is supported only if the unified control group hierarchy is used and disables + MemoryLimit=. ++ ++ Units may have their children use a default memory.min value by specifying ++ DefaultMemoryMin=, which has the same semantics as MemoryMin=. This setting ++ does not affect memory.min in the unit itself. + + + +@@ -306,8 +310,8 @@ + This setting is supported only if the unified control group hierarchy is used and disables + MemoryLimit=. + +- Units may can have their children use a default memory.low value by specifying +- DefaultMemoryLow=, which has the same usage as MemoryLow=. This setting ++ Units may have their children use a default memory.low value by specifying ++ DefaultMemoryLow=, which has the same semantics as MemoryLow=. This setting + does not affect memory.low in the unit itself. + + diff --git a/SOURCES/0410-cgroup-Readd-some-plumbing-for-DefaultMemoryMin.patch b/SOURCES/0410-cgroup-Readd-some-plumbing-for-DefaultMemoryMin.patch new file mode 100644 index 0000000..13f3fec --- /dev/null +++ b/SOURCES/0410-cgroup-Readd-some-plumbing-for-DefaultMemoryMin.patch @@ -0,0 +1,68 @@ +From dbdba4f2998313949e7c5f5a47f4ef938e759a86 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Fri, 3 May 2019 08:19:05 -0400 +Subject: [PATCH] cgroup: Readd some plumbing for DefaultMemoryMin + +Somehow these got lost in the previous PR, rendering DefaultMemoryMin +not very useful. + +(cherry picked from commit 7e7223b3d57c950b399352a92e1d817f7c463602) + +Related: #1763435 +--- + src/core/dbus-cgroup.c | 1 + + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/shared/bus-unit-util.c | 2 +- + src/shared/bus-util.c | 2 +- + 4 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index e1278c317a..e34ff3a016 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -354,6 +354,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), + SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0), + SD_BUS_PROPERTY("DefaultMemoryLow", "t", NULL, offsetof(CGroupContext, default_memory_low), 0), ++ SD_BUS_PROPERTY("DefaultMemoryMin", "t", NULL, offsetof(CGroupContext, default_memory_min), 0), + SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0), + SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0), + SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0), +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 43cc78fdea..6d21b2e433 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -172,6 +172,7 @@ $1.CPUQuota, config_parse_cpu_quota, 0, + $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.DefaultMemoryMin, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.DefaultMemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context) + $1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context) +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index f88730a85d..77788f0fe2 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -429,7 +429,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons + return 1; + } + +- if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { ++ if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { + + if (isempty(eq) || streq(eq, "infinity")) { + r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index 0ba2712deb..ff0e800347 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -774,7 +774,7 @@ int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all + + print_property(name, "%s", "[not set]"); + +- else if ((STR_IN_SET(name, "DefaultMemoryLow", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || ++ else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) || + (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) || + (startswith(name, "Limit") && u == (uint64_t) -1) || + (startswith(name, "DefaultLimit") && u == (uint64_t) -1)) diff --git a/SOURCES/0411-cgroup-Support-0-value-for-memory-protection-directi.patch b/SOURCES/0411-cgroup-Support-0-value-for-memory-protection-directi.patch new file mode 100644 index 0000000..5e1e1f2 --- /dev/null +++ b/SOURCES/0411-cgroup-Support-0-value-for-memory-protection-directi.patch @@ -0,0 +1,86 @@ +From 614b43fcad3a16dfde5ad606b43c3aa1adacc30a Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Fri, 3 May 2019 08:32:41 -0400 +Subject: [PATCH] cgroup: Support 0-value for memory protection directives + +These make sense to be explicitly set at 0 (which has a different effect +than the default, since it can affect processing of `DefaultMemoryXXX`). + +Without this, it's not easily possible to relinquish memory protection +for a subtree, which is not great. + +(cherry picked from commit 22bf131be278b95a4a204514d37a4344cf6365c6) + +Related: #1763435 +--- + src/core/dbus-cgroup.c | 17 +++++++++-------- + src/core/load-fragment.c | 2 +- + 2 files changed, 10 insertions(+), 9 deletions(-) + +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index e34ff3a016..39778a8dd4 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -606,6 +606,7 @@ BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_O + BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID); + BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID); + BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1); ++BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memory_scale, 0); + BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0); + BUS_DEFINE_SET_CGROUP_LIMIT(tasks_max, CGROUP_MASK_PIDS, system_tasks_max_scale, 1); + #pragma GCC diagnostic pop +@@ -665,16 +666,16 @@ int bus_cgroup_set_property( + return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error); + + if (streq(name, "MemoryMin")) +- return bus_cgroup_set_memory(u, name, &c->memory_min, message, flags, error); ++ return bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error); + + if (streq(name, "MemoryLow")) +- return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error); ++ return bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error); + + if (streq(name, "DefaultMemoryMin")) +- return bus_cgroup_set_memory(u, name, &c->default_memory_min, message, flags, error); ++ return bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error); + + if (streq(name, "DefaultMemoryLow")) +- return bus_cgroup_set_memory(u, name, &c->default_memory_low, message, flags, error); ++ return bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error); + + if (streq(name, "MemoryHigh")) + return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error); +@@ -689,16 +690,16 @@ int bus_cgroup_set_property( + return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error); + + if (streq(name, "MemoryMinScale")) +- return bus_cgroup_set_memory_scale(u, name, &c->memory_min, message, flags, error); ++ return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error); + + if (streq(name, "MemoryLowScale")) +- return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error); ++ return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error); + + if (streq(name, "DefaultMemoryMinScale")) +- return bus_cgroup_set_memory_scale(u, name, &c->default_memory_min, message, flags, error); ++ return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error); + + if (streq(name, "DefaultMemoryLowScale")) +- return bus_cgroup_set_memory_scale(u, name, &c->default_memory_low, message, flags, error); ++ return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error); + + if (streq(name, "MemoryHighScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error); +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 3b8ee6b124..33fdb82754 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -3090,7 +3090,7 @@ int config_parse_memory_limit( + bytes = physical_memory_scale(r, 100U); + + if (bytes >= UINT64_MAX || +- (bytes <= 0 && !streq(lvalue, "MemorySwapMax"))) { ++ (bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range, ignoring.", rvalue); + return 0; + } diff --git a/SOURCES/0412-cgroup-Test-that-it-s-possible-to-set-memory-protect.patch b/SOURCES/0412-cgroup-Test-that-it-s-possible-to-set-memory-protect.patch new file mode 100644 index 0000000..bb8f9c9 --- /dev/null +++ b/SOURCES/0412-cgroup-Test-that-it-s-possible-to-set-memory-protect.patch @@ -0,0 +1,58 @@ +From ef4157dab3d267c33ec2a06ae9bb5e4c87f785a6 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Fri, 3 May 2019 08:40:11 -0400 +Subject: [PATCH] cgroup: Test that it's possible to set memory protection to 0 + again + +The previous commit fixes this up, and this should prevent it +regressing. + +(cherry picked from commit 465ace74d9820824968ab5e82c81e42c2f1894b0) + +Related: #1763435 +--- + src/test/test-cgroup-unit-default.c | 6 +++--- + test/dml-passthrough-set-ml.service | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/test/test-cgroup-unit-default.c b/src/test/test-cgroup-unit-default.c +index 54f7d50c45..1938609cee 100644 +--- a/src/test/test-cgroup-unit-default.c ++++ b/src/test/test-cgroup-unit-default.c +@@ -39,7 +39,7 @@ static int test_default_memory_low(void) { + * 1. dml-passthrough.slice sets MemoryLow=100. This should not affect its children, as only + * DefaultMemoryLow is propagated, not MemoryLow. As such, all leaf services should end up with + * memory.low as 50, inherited from dml.slice, *except* for dml-passthrough-set-ml.service, which +- * should have the value of 25, as it has MemoryLow explicitly set. ++ * should have the value of 0, as it has MemoryLow explicitly set. + * + * ┌───────────┐ + * │ dml.slice │ +@@ -49,7 +49,7 @@ static int test_default_memory_low(void) { + * │ dml-passthrough.slice │ + * └───────────┬───────────┘ + * ┌───────────────────────────────────┼───────────────────────────────────┐ +- * no new settings DefaultMemoryLow=15 MemoryLow=25 ++ * no new settings DefaultMemoryLow=15 MemoryLow=0 + * ┌───────────────┴───────────────┐ ┌────────────────┴────────────────┐ ┌───────────────┴────────────────┐ + * │ dml-passthrough-empty.service │ │ dml-passthrough-set-dml.service │ │ dml-passthrough-set-ml.service │ + * └───────────────────────────────┘ └─────────────────────────────────┘ └────────────────────────────────┘ +@@ -122,7 +122,7 @@ static int test_default_memory_low(void) { + assert_se(unit_get_ancestor_memory_low(dml_passthrough) == 100); + assert_se(unit_get_ancestor_memory_low(dml_passthrough_empty) == dml_tree_default); + assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_dml) == 50); +- assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 25); ++ assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 0); + + assert_se(unit_get_ancestor_memory_low(dml_override) == dml_tree_default); + assert_se(unit_get_ancestor_memory_low(dml_override_empty) == 10); +diff --git a/test/dml-passthrough-set-ml.service b/test/dml-passthrough-set-ml.service +index 2abd591389..2e568b5deb 100644 +--- a/test/dml-passthrough-set-ml.service ++++ b/test/dml-passthrough-set-ml.service +@@ -5,4 +5,4 @@ Description=DML passthrough set ML service + Slice=dml-passthrough.slice + Type=oneshot + ExecStart=/bin/true +-MemoryLow=25 ++MemoryLow=0 diff --git a/SOURCES/0413-cgroup-Check-ancestor-memory-min-for-unified-memory-.patch b/SOURCES/0413-cgroup-Check-ancestor-memory-min-for-unified-memory-.patch new file mode 100644 index 0000000..7761e7f --- /dev/null +++ b/SOURCES/0413-cgroup-Check-ancestor-memory-min-for-unified-memory-.patch @@ -0,0 +1,28 @@ +From 49c990010f48b429b52f73f54d70d529f0d2c7fe Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Mon, 30 Sep 2019 18:24:26 +0100 +Subject: [PATCH] cgroup: Check ancestor memory min for unified memory config + +Otherwise we might not enable it when we should, ie. DefaultMemoryMin is +set in a parent, but not MemoryMin in the current unit. + +(cherry picked from commit 7c9d2b79935d413389a603918a711df75acd3f48) + +Related: #1763435 +--- + src/core/cgroup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index d40f9cbc2a..4299923754 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -851,7 +851,7 @@ static bool unit_has_unified_memory_config(Unit *u) { + c = unit_get_cgroup_context(u); + assert(c); + +- return c->memory_min > 0 || unit_get_ancestor_memory_low(u) > 0 || ++ return unit_get_ancestor_memory_min(u) > 0 || unit_get_ancestor_memory_low(u) > 0 || + c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || + c->memory_swap_max != CGROUP_LIMIT_MAX; + } diff --git a/SOURCES/0414-cgroup-Respect-DefaultMemoryMin-when-setting-memory..patch b/SOURCES/0414-cgroup-Respect-DefaultMemoryMin-when-setting-memory..patch new file mode 100644 index 0000000..4ded71a --- /dev/null +++ b/SOURCES/0414-cgroup-Respect-DefaultMemoryMin-when-setting-memory..patch @@ -0,0 +1,31 @@ +From 9c04746fb35b32b592fe14cab43782db9e0dee5c Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Mon, 30 Sep 2019 18:25:09 +0100 +Subject: [PATCH] cgroup: Respect DefaultMemoryMin when setting memory.min + +This is an oversight from https://github.com/systemd/systemd/pull/12332. + +Sadly the tests didn't catch it since it requires a real cgroup +hierarchy to see, and it wasn't seen in prod since we're only currently +using DefaultMemoryLow, not DefaultMemoryMin. :-( + +(cherry picked from commit 64fe532e90b3e99bf7821ded8a1107c239099e40) + +Related: #1763435 +--- + src/core/cgroup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 4299923754..7a9857adad 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1111,7 +1111,7 @@ static void cgroup_context_apply( + log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max); + } + +- cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min); ++ cgroup_apply_unified_memory_limit(u, "memory.min", unit_get_ancestor_memory_min(u)); + cgroup_apply_unified_memory_limit(u, "memory.low", unit_get_ancestor_memory_low(u)); + cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high); + cgroup_apply_unified_memory_limit(u, "memory.max", max); diff --git a/SOURCES/0415-cgroup-Mark-memory-protections-as-explicitly-set-in-.patch b/SOURCES/0415-cgroup-Mark-memory-protections-as-explicitly-set-in-.patch new file mode 100644 index 0000000..c38a93e --- /dev/null +++ b/SOURCES/0415-cgroup-Mark-memory-protections-as-explicitly-set-in-.patch @@ -0,0 +1,108 @@ +From 4e7a3af028dfe6b5a4a85c31d670df73c08a0719 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Mon, 30 Sep 2019 18:36:13 +0100 +Subject: [PATCH] cgroup: Mark memory protections as explicitly set in + transient units + +A later version of the DefaultMemory{Low,Min} patch changed these to +require explicitly setting memory_foo_set, but we only set that in +load-fragment, not dbus-cgroup. + +Without these, we may fall back to either DefaultMemoryFoo or +CGROUP_LIMIT_MIN when we really shouldn't. + +(cherry picked from commit 184e989d7da4648bd36511ffa28a9f2b469589d1) + +Related: #1763435 +--- + src/core/dbus-cgroup.c | 64 +++++++++++++++++++++++++++++++----------- + 1 file changed, 48 insertions(+), 16 deletions(-) + +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 39778a8dd4..de85a4851b 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -665,17 +665,33 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryAccounting")) + return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error); + +- if (streq(name, "MemoryMin")) +- return bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error); ++ if (streq(name, "MemoryMin")) { ++ r = bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error); ++ if (r > 0) ++ c->memory_min_set = true; ++ return r; ++ } + +- if (streq(name, "MemoryLow")) +- return bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error); ++ if (streq(name, "MemoryLow")) { ++ r = bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error); ++ if (r > 0) ++ c->memory_low_set = true; ++ return r; ++ } + +- if (streq(name, "DefaultMemoryMin")) +- return bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error); ++ if (streq(name, "DefaultMemoryMin")) { ++ r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error); ++ if (r > 0) ++ c->default_memory_min_set = true; ++ return r; ++ } + +- if (streq(name, "DefaultMemoryLow")) +- return bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error); ++ if (streq(name, "DefaultMemoryLow")) { ++ r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error); ++ if (r > 0) ++ c->default_memory_low_set = true; ++ return r; ++ } + + if (streq(name, "MemoryHigh")) + return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error); +@@ -689,17 +705,33 @@ int bus_cgroup_set_property( + if (streq(name, "MemoryLimit")) + return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error); + +- if (streq(name, "MemoryMinScale")) +- return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error); ++ if (streq(name, "MemoryMinScale")) { ++ r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error); ++ if (r > 0) ++ c->memory_min_set = true; ++ return r; ++ } + +- if (streq(name, "MemoryLowScale")) +- return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error); ++ if (streq(name, "MemoryLowScale")) { ++ r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error); ++ if (r > 0) ++ c->memory_low_set = true; ++ return r; ++ } + +- if (streq(name, "DefaultMemoryMinScale")) +- return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error); ++ if (streq(name, "DefaultMemoryMinScale")) { ++ r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error); ++ if (r > 0) ++ c->default_memory_min_set = true; ++ return r; ++ } + +- if (streq(name, "DefaultMemoryLowScale")) +- return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error); ++ if (streq(name, "DefaultMemoryLowScale")) { ++ r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error); ++ if (r > 0) ++ c->default_memory_low_set = true; ++ return r; ++ } + + if (streq(name, "MemoryHighScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error); diff --git a/SOURCES/0416-meson-allow-setting-the-version-string-during-config.patch b/SOURCES/0416-meson-allow-setting-the-version-string-during-config.patch new file mode 100644 index 0000000..9e082d2 --- /dev/null +++ b/SOURCES/0416-meson-allow-setting-the-version-string-during-config.patch @@ -0,0 +1,86 @@ +From 0065f2bf838dd0c24ec7be41439b4c0ba650029c Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 19 Feb 2020 15:36:13 +0100 +Subject: [PATCH] meson: allow setting the version string during configuration + +Very loosely based on upstream commits e1ca734edd17a90a325d5b566a4ea96e66c206e5 +and 681bd2c524ed71ac04045c90884ba8d55eee7b66. + +Resolves: #1804252 +--- + meson.build | 15 +++++++++++---- + meson_options.txt | 3 +++ + src/udev/udev-ctrl.c | 5 ++++- + 3 files changed, 18 insertions(+), 5 deletions(-) + +diff --git a/meson.build b/meson.build +index c8ae1e15bd..0ba3f924ea 100644 +--- a/meson.build ++++ b/meson.build +@@ -15,17 +15,24 @@ project('systemd', 'c', + libsystemd_version = '0.23.0' + libudev_version = '1.6.11' + ++dist_version = get_option('version-tag') ++if dist_version == '' ++ dist_version = meson.project_version() ++else ++ dist_version = meson.project_version() + ' (' + dist_version + ')' ++endif ++ + # We need the same data in two different formats, ugh! + # Also, for hysterical reasons, we use different variable + # names, sometimes. Not all variables are included in every + # set. Ugh, ugh, ugh! + conf = configuration_data() +-conf.set_quoted('PACKAGE_STRING', meson.project_name() + ' ' + meson.project_version()) +-conf.set_quoted('PACKAGE_VERSION', meson.project_version()) ++conf.set_quoted('PACKAGE_STRING', meson.project_name() + ' ' + dist_version) ++conf.set_quoted('PACKAGE_VERSION', dist_version) + + substs = configuration_data() + substs.set('PACKAGE_URL', 'https://www.freedesktop.org/wiki/Software/systemd') +-substs.set('PACKAGE_VERSION', meson.project_version()) ++substs.set('PACKAGE_VERSION', dist_version) + + ##################################################################### + +@@ -2871,7 +2878,7 @@ run_target( + ############################################################ + + status = [ +- '@0@ @1@'.format(meson.project_name(), meson.project_version()), ++ '@0@ @1@'.format(meson.project_name(), dist_version), + + 'split /usr: @0@'.format(split_usr), + 'split bin-sbin: @0@'.format(split_bin), +diff --git a/meson_options.txt b/meson_options.txt +index 563b11f0a2..0996891177 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -1,6 +1,9 @@ + # -*- mode: meson -*- + # SPDX-License-Identifier: LGPL-2.1+ + ++option('version-tag', type : 'string', ++ description : 'override the version string') ++ + option('split-usr', type : 'combo', choices : ['auto', 'true', 'false'], + description : '''/bin, /sbin aren't symlinks into /usr''') + option('split-bin', type : 'combo', choices : ['auto', 'true', 'false'], +diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c +index efe7297f04..5382ce0d26 100644 +--- a/src/udev/udev-ctrl.c ++++ b/src/udev/udev-ctrl.c +@@ -239,7 +239,10 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int + int err = 0; + + memzero(&ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); +- strcpy(ctrl_msg_wire.version, "udev-" PACKAGE_VERSION); ++ /* jsynacek: As PACKAGE_VERSION is populated from the spec file with %{version}-%{release}, ++ * it might not fit entirely into the version field. */ ++ strncpy(ctrl_msg_wire.version, "udev-" PACKAGE_VERSION, 16-5); ++ ctrl_msg_wire.version[15] = '\0'; + ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; + ctrl_msg_wire.type = type; + diff --git a/SOURCES/0417-core-don-t-consider-SERVICE_SKIP_CONDITION-for-abnor.patch b/SOURCES/0417-core-don-t-consider-SERVICE_SKIP_CONDITION-for-abnor.patch new file mode 100644 index 0000000..3ef1273 --- /dev/null +++ b/SOURCES/0417-core-don-t-consider-SERVICE_SKIP_CONDITION-for-abnor.patch @@ -0,0 +1,170 @@ +From 41346615264e01c6ff6118e09cf3ac4b4c71e89d Mon Sep 17 00:00:00 2001 +From: Anita Zhang +Date: Wed, 10 Jun 2020 01:18:00 -0700 +Subject: [PATCH] core: don't consider SERVICE_SKIP_CONDITION for abnormal or + failure restarts + +Fixes: #16115 +(cherry picked from commit bb9244781c6fc7608f7cac910269f8987b8adc01) + +Related: #1737283 +--- + src/core/service.c | 4 +-- + test/TEST-51-ISSUE-16115/Makefile | 1 + + test/TEST-51-ISSUE-16115/repro-1.service | 9 +++++ + test/TEST-51-ISSUE-16115/repro-2.service | 9 +++++ + test/TEST-51-ISSUE-16115/test.sh | 46 ++++++++++++++++++++++++ + test/TEST-51-ISSUE-16115/testsuite.sh | 19 ++++++++++ + test/test-functions | 2 +- + 7 files changed, 87 insertions(+), 3 deletions(-) + create mode 120000 test/TEST-51-ISSUE-16115/Makefile + create mode 100644 test/TEST-51-ISSUE-16115/repro-1.service + create mode 100644 test/TEST-51-ISSUE-16115/repro-2.service + create mode 100755 test/TEST-51-ISSUE-16115/test.sh + create mode 100755 test/TEST-51-ISSUE-16115/testsuite.sh + +diff --git a/src/core/service.c b/src/core/service.c +index 92be4280f6..1d98ee37fd 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -1637,10 +1637,10 @@ static bool service_shall_restart(Service *s) { + return s->result == SERVICE_SUCCESS; + + case SERVICE_RESTART_ON_FAILURE: +- return s->result != SERVICE_SUCCESS; ++ return !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_SKIP_CONDITION); + + case SERVICE_RESTART_ON_ABNORMAL: +- return !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_FAILURE_EXIT_CODE); ++ return !IN_SET(s->result, SERVICE_SUCCESS, SERVICE_FAILURE_EXIT_CODE, SERVICE_SKIP_CONDITION); + + case SERVICE_RESTART_ON_WATCHDOG: + return s->result == SERVICE_FAILURE_WATCHDOG; +diff --git a/test/TEST-51-ISSUE-16115/Makefile b/test/TEST-51-ISSUE-16115/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-51-ISSUE-16115/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-51-ISSUE-16115/repro-1.service b/test/TEST-51-ISSUE-16115/repro-1.service +new file mode 100644 +index 0000000000..96ecabe234 +--- /dev/null ++++ b/test/TEST-51-ISSUE-16115/repro-1.service +@@ -0,0 +1,9 @@ ++[Unit] ++Description=Issue 16115 Repro with on-abnormal ++ ++[Service] ++Type=simple ++Restart=on-abnormal ++ExecCondition=/bin/false ++ExecStart=sleep 100 ++RestartSec=1 +diff --git a/test/TEST-51-ISSUE-16115/repro-2.service b/test/TEST-51-ISSUE-16115/repro-2.service +new file mode 100644 +index 0000000000..6015ad8080 +--- /dev/null ++++ b/test/TEST-51-ISSUE-16115/repro-2.service +@@ -0,0 +1,9 @@ ++[Unit] ++Description=Issue 16115 Repro with on-failure ++ ++[Service] ++Type=simple ++Restart=on-failure ++ExecCondition=/bin/false ++ExecStart=sleep 100 ++RestartSec=1 +diff --git a/test/TEST-51-ISSUE-16115/test.sh b/test/TEST-51-ISSUE-16115/test.sh +new file mode 100755 +index 0000000000..09ac96ffce +--- /dev/null ++++ b/test/TEST-51-ISSUE-16115/test.sh +@@ -0,0 +1,46 @@ ++#!/usr/bin/env bash ++set -e ++TEST_DESCRIPTION="Test ExecCondition= does not restart on abnormal or failure" ++. $TEST_BASE_DIR/test-functions ++ ++test_setup() { ++ create_empty_image ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ ++ ( ++ LOG_LEVEL=5 ++ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) ++ ++ setup_basic_environment ++ ++ # mask some services that we do not want to run in these tests ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service < /testok ++ ++exit 0 +diff --git a/test/test-functions b/test/test-functions +index 7c4230b078..4d7832b1fb 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -23,7 +23,7 @@ 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" ++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 env" + DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find" + + STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))" diff --git a/SOURCES/0418-selinux-do-preprocessor-check-only-in-selinux-access.patch b/SOURCES/0418-selinux-do-preprocessor-check-only-in-selinux-access.patch new file mode 100644 index 0000000..d6b7c1a --- /dev/null +++ b/SOURCES/0418-selinux-do-preprocessor-check-only-in-selinux-access.patch @@ -0,0 +1,39 @@ +From 7301b170b266225f091e95ff52b3a95ff9776d13 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 3 Apr 2020 09:13:59 +0200 +Subject: [PATCH] selinux: do preprocessor check only in selinux-access.c + +This has the advantage that mac_selinux_access_check() can be used as a +function in all contexts. For example, parameters passed to it won't be +reported as unused if the "function" call is replaced with 0 on SELinux +disabled builds. + +(cherry picked from commit 08deac6e3e9119aeb966375f94695e4aa14ffb1c) + +Related: #1830861 +--- + src/core/selinux-access.h | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h +index 59f2e60c77..46a657a4b4 100644 +--- a/src/core/selinux-access.h ++++ b/src/core/selinux-access.h +@@ -12,17 +12,8 @@ + + int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error); + +-#if HAVE_SELINUX +- + #define mac_selinux_access_check(message, permission, error) \ + mac_selinux_generic_access_check((message), NULL, (permission), (error)) + + #define mac_selinux_unit_access_check(unit, message, permission, error) \ + mac_selinux_generic_access_check((message), unit_label_path(unit), (permission), (error)) +- +-#else +- +-#define mac_selinux_access_check(message, permission, error) 0 +-#define mac_selinux_unit_access_check(unit, message, permission, error) 0 +- +-#endif diff --git a/SOURCES/0419-basic-cgroup-util-introduce-cg_get_keyed_attribute_f.patch b/SOURCES/0419-basic-cgroup-util-introduce-cg_get_keyed_attribute_f.patch new file mode 100644 index 0000000..adcc7a4 --- /dev/null +++ b/SOURCES/0419-basic-cgroup-util-introduce-cg_get_keyed_attribute_f.patch @@ -0,0 +1,144 @@ +From 5aefc153b25b42a80942e5c367ce143817551870 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Wed, 29 Apr 2020 17:40:22 +0200 +Subject: [PATCH] basic/cgroup-util: introduce cg_get_keyed_attribute_full() + +Callers of cg_get_keyed_attribute_full() can now specify via the flag whether the +missing keyes in cgroup attribute file are OK or not. Also the wrappers for both +strict and graceful version are provided. + +(cherry picked from commit 25a1f04c682260bb9b96e25bdf33665d6172db98) + +Related: #1830861 +--- + src/basic/cgroup-util.c | 13 ++++++++++--- + src/basic/cgroup-util.h | 24 +++++++++++++++++++++++- + src/test/test-cgroup-util.c | 20 ++++++++++++++++++++ + 3 files changed, 53 insertions(+), 4 deletions(-) + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index 92bc1f2543..4c0b73850d 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -2021,12 +2021,13 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri + return read_one_line_file(p, ret); + } + +-int cg_get_keyed_attribute( ++int cg_get_keyed_attribute_full( + const char *controller, + const char *path, + const char *attribute, + char **keys, +- char **ret_values) { ++ char **ret_values, ++ CGroupKeyMode mode) { + + _cleanup_free_ char *filename = NULL, *contents = NULL; + const char *p; +@@ -2086,7 +2087,10 @@ int cg_get_keyed_attribute( + p += strspn(p, NEWLINE); + } + +- r = -ENXIO; ++ if (mode & CG_KEY_MODE_GRACEFUL) ++ goto done; ++ else ++ r = -ENXIO; + + fail: + for (i = 0; i < n; i++) +@@ -2096,6 +2100,9 @@ fail: + + done: + memcpy(ret_values, v, sizeof(char*) * n); ++ if (mode & CG_KEY_MODE_GRACEFUL) ++ return n_done; ++ + return 0; + + } +diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h +index b414600dca..0673f4b4ce 100644 +--- a/src/basic/cgroup-util.h ++++ b/src/basic/cgroup-util.h +@@ -167,9 +167,31 @@ int cg_attach(const char *controller, const char *path, pid_t pid); + int cg_attach_fallback(const char *controller, const char *path, pid_t pid); + int cg_create_and_attach(const char *controller, const char *path, pid_t pid); + ++typedef enum { ++ CG_KEY_MODE_GRACEFUL = 1 << 0, ++} CGroupKeyMode; ++ + int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); + int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); +-int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values); ++int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode); ++ ++static inline int cg_get_keyed_attribute( ++ const char *controller, ++ const char *path, ++ const char *attribute, ++ char **keys, ++ char **ret_values) { ++ return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0); ++} ++ ++static inline int cg_get_keyed_attribute_graceful( ++ const char *controller, ++ const char *path, ++ const char *attribute, ++ char **keys, ++ char **ret_values) { ++ return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL); ++} + + int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid); + +diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c +index d49356315e..60d7bb19cb 100644 +--- a/src/test/test-cgroup-util.c ++++ b/src/test/test-cgroup-util.c +@@ -421,22 +421,42 @@ static void test_cg_get_keyed_attribute(void) { + } + + assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO); ++ assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0); + assert_se(val == NULL); + + assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0); ++ free(val); ++ ++ assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 1); + log_info("cpu /init.scope cpu.stat [usage_usec] → \"%s\"", val); + + assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO); ++ assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == 1); ++ assert(vals3[0] && !vals3[1]); ++ free(vals3[0]); + + assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO); ++ assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == 1); ++ assert(vals3[0] && !vals3[1]); ++ free(vals3[0]); + + assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", + STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 0); ++ for (i = 0; i < 3; i++) ++ free(vals3[i]); ++ ++ assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", ++ STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 3); + log_info("cpu /init.scope cpu.stat [usage_usec user_usec system_usec] → \"%s\", \"%s\", \"%s\"", + vals3[0], vals3[1], vals3[2]); + + assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", + STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 0); ++ for (i = 0; i < 3; i++) ++ free(vals3a[i]); ++ ++ assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", ++ STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 3); + log_info("cpu /init.scope cpu.stat [system_usec user_usec usage_usec] → \"%s\", \"%s\", \"%s\"", + vals3a[0], vals3a[1], vals3a[2]); + diff --git a/SOURCES/0420-shared-add-generic-logic-for-waiting-for-a-unit-to-e.patch b/SOURCES/0420-shared-add-generic-logic-for-waiting-for-a-unit-to-e.patch new file mode 100644 index 0000000..39ba3d7 --- /dev/null +++ b/SOURCES/0420-shared-add-generic-logic-for-waiting-for-a-unit-to-e.patch @@ -0,0 +1,526 @@ +From 5e422a9cea38bd5c7ce54c7bbac612c04418dc41 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Apr 2019 18:54:59 +0200 +Subject: [PATCH] shared: add generic logic for waiting for a unit to enter + some state + +This is a generic implementation of a client-side logic of waiting until +a unit enters or leaves some state. + +This is a more generic implementation of the WaitContext logic currently +in systemctl.c, and is supposed to replace it (a later commit does +this). It's similar to bus-wait-for-jobs.c and we probably should fold +that one into it later on. + +This code is more powerful and cleaner than the WaitContext logic +however. In addition to waiting for a unit to exit this also allows us +to wait for a unit to leave the "maintainance" state. + +This commit only implements the generic logic, and adds no users of it +yet. + +(cherry picked from commit 3572d3df8f822d4cf1601428401a837f723771cf) + +Related: #1830861 +--- + src/shared/bus-wait-for-units.c | 434 ++++++++++++++++++++++++++++++++ + src/shared/bus-wait-for-units.h | 35 +++ + src/shared/meson.build | 2 + + 3 files changed, 471 insertions(+) + create mode 100644 src/shared/bus-wait-for-units.c + create mode 100644 src/shared/bus-wait-for-units.h + +diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c +new file mode 100644 +index 0000000000..d07f491e93 +--- /dev/null ++++ b/src/shared/bus-wait-for-units.c +@@ -0,0 +1,434 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "bus-util.h" ++#include "bus-wait-for-units.h" ++#include "hashmap.h" ++#include "string-util.h" ++#include "strv.h" ++#include "unit-def.h" ++ ++typedef struct WaitForItem { ++ BusWaitForUnits *parent; ++ ++ BusWaitForUnitsFlags flags; ++ ++ char *bus_path; ++ ++ sd_bus_slot *slot_get_all; ++ sd_bus_slot *slot_properties_changed; ++ ++ bus_wait_for_units_unit_callback unit_callback; ++ void *userdata; ++ ++ char *active_state; ++ uint32_t job_id; ++ char *clean_result; ++} WaitForItem; ++ ++typedef struct BusWaitForUnits { ++ sd_bus *bus; ++ sd_bus_slot *slot_disconnected; ++ ++ Hashmap *items; ++ ++ bus_wait_for_units_ready_callback ready_callback; ++ void *userdata; ++ ++ WaitForItem *current; ++ ++ BusWaitForUnitsState state; ++ bool has_failed:1; ++} BusWaitForUnits; ++ ++static WaitForItem *wait_for_item_free(WaitForItem *item) { ++ int r; ++ ++ if (!item) ++ return NULL; ++ ++ if (item->parent) { ++ if (FLAGS_SET(item->flags, BUS_WAIT_REFFED) && item->bus_path && item->parent->bus) { ++ r = sd_bus_call_method_async( ++ item->parent->bus, ++ NULL, ++ "org.freedesktop.systemd1", ++ item->bus_path, ++ "org.freedesktop.systemd1.Unit", ++ "Unref", ++ NULL, ++ NULL, ++ NULL); ++ if (r < 0) ++ log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path); ++ } ++ ++ assert_se(hashmap_remove(item->parent->items, item->bus_path) == item); ++ ++ if (item->parent->current == item) ++ item->parent->current = NULL; ++ } ++ ++ sd_bus_slot_unref(item->slot_properties_changed); ++ sd_bus_slot_unref(item->slot_get_all); ++ ++ free(item->bus_path); ++ free(item->active_state); ++ free(item->clean_result); ++ ++ return mfree(item); ++} ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free); ++ ++static void bus_wait_for_units_clear(BusWaitForUnits *d) { ++ WaitForItem *item; ++ ++ assert(d); ++ ++ d->slot_disconnected = sd_bus_slot_unref(d->slot_disconnected); ++ d->bus = sd_bus_unref(d->bus); ++ ++ while ((item = hashmap_first(d->items))) { ++ d->current = item; ++ ++ item->unit_callback(d, item->bus_path, false, item->userdata); ++ wait_for_item_free(item); ++ } ++ ++ d->items = hashmap_free(d->items); ++} ++ ++static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { ++ BusWaitForUnits *d = userdata; ++ ++ assert(m); ++ assert(d); ++ ++ log_error("Warning! D-Bus connection terminated."); ++ ++ bus_wait_for_units_clear(d); ++ ++ if (d->ready_callback) ++ d->ready_callback(d, false, d->userdata); ++ else /* If no ready callback is specified close the connection so that the event loop exits */ ++ sd_bus_close(sd_bus_message_get_bus(m)); ++ ++ return 0; ++} ++ ++int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret) { ++ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *d = NULL; ++ int r; ++ ++ assert(bus); ++ assert(ret); ++ ++ d = new(BusWaitForUnits, 1); ++ if (!d) ++ return -ENOMEM; ++ ++ *d = (BusWaitForUnits) { ++ .state = BUS_WAIT_SUCCESS, ++ .bus = sd_bus_ref(bus), ++ }; ++ ++ 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; ++} ++ ++BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d) { ++ if (!d) ++ return NULL; ++ ++ bus_wait_for_units_clear(d); ++ sd_bus_slot_unref(d->slot_disconnected); ++ sd_bus_unref(d->bus); ++ ++ return mfree(d); ++} ++ ++static bool bus_wait_for_units_is_ready(BusWaitForUnits *d) { ++ assert(d); ++ ++ if (!d->bus) /* Disconnected? */ ++ return true; ++ ++ return hashmap_isempty(d->items); ++} ++ ++void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata) { ++ assert(d); ++ ++ d->ready_callback = callback; ++ d->userdata = userdata; ++} ++ ++static void bus_wait_for_units_check_ready(BusWaitForUnits *d) { ++ assert(d); ++ ++ if (!bus_wait_for_units_is_ready(d)) ++ return; ++ ++ d->state = d->has_failed ? BUS_WAIT_FAILURE : BUS_WAIT_SUCCESS; ++ ++ if (d->ready_callback) ++ d->ready_callback(d, d->state, d->userdata); ++} ++ ++static void wait_for_item_check_ready(WaitForItem *item) { ++ BusWaitForUnits *d; ++ ++ assert(item); ++ assert(d = item->parent); ++ ++ if (FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END)) { ++ ++ if (item->clean_result && !streq(item->clean_result, "success")) ++ d->has_failed = true; ++ ++ if (!item->active_state || streq(item->active_state, "maintenance")) ++ return; ++ } ++ ++ if (FLAGS_SET(item->flags, BUS_WAIT_NO_JOB) && item->job_id != 0) ++ return; ++ ++ if (FLAGS_SET(item->flags, BUS_WAIT_FOR_INACTIVE)) { ++ ++ if (streq_ptr(item->active_state, "failed")) ++ d->has_failed = true; ++ else if (!streq_ptr(item->active_state, "inactive")) ++ return; ++ } ++ ++ if (item->unit_callback) { ++ d->current = item; ++ item->unit_callback(d, item->bus_path, true, item->userdata); ++ } ++ ++ wait_for_item_free(item); ++ ++ bus_wait_for_units_check_ready(d); ++} ++ ++static int property_map_job( ++ sd_bus *bus, ++ const char *member, ++ sd_bus_message *m, ++ sd_bus_error *error, ++ void *userdata) { ++ ++ WaitForItem *item = userdata; ++ const char *path; ++ uint32_t id; ++ int r; ++ ++ assert(item); ++ ++ r = sd_bus_message_read(m, "(uo)", &id, &path); ++ if (r < 0) ++ return r; ++ ++ item->job_id = id; ++ return 0; ++} ++ ++static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) { ++ ++ static const struct bus_properties_map map[] = { ++ { "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) }, ++ { "Job", "(uo)", property_map_job, 0 }, ++ { "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) }, ++ {} ++ }; ++ ++ int r; ++ ++ assert(item); ++ assert(m); ++ ++ r = bus_message_map_all_properties(m, map, BUS_MAP_STRDUP, NULL, item); ++ if (r < 0) ++ return r; ++ ++ wait_for_item_check_ready(item); ++ return 0; ++} ++ ++static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) { ++ WaitForItem *item = userdata; ++ const char *interface; ++ int r; ++ ++ assert(item); ++ ++ r = sd_bus_message_read(m, "s", &interface); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to parse PropertiesChanged signal: %m"); ++ return 0; ++ } ++ ++ if (!streq(interface, "org.freedesktop.systemd1.Unit")) ++ return 0; ++ ++ r = wait_for_item_parse_properties(item, m); ++ if (r < 0) ++ log_debug_errno(r, "Failed to process PropertiesChanged signal: %m"); ++ ++ return 0; ++} ++ ++static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *error) { ++ WaitForItem *item = userdata; ++ int r; ++ ++ assert(item); ++ ++ if (sd_bus_error_is_set(error)) { ++ BusWaitForUnits *d = item->parent; ++ ++ d->has_failed = true; ++ ++ log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s", ++ item->bus_path, error->message); ++ ++ d->current = item; ++ item->unit_callback(d, item->bus_path, false, item->userdata); ++ wait_for_item_free(item); ++ ++ bus_wait_for_units_check_ready(d); ++ return 0; ++ } ++ ++ r = wait_for_item_parse_properties(item, m); ++ if (r < 0) ++ log_debug_errno(r, "Failed to process GetAll method reply: %m"); ++ ++ return 0; ++} ++ ++int bus_wait_for_units_add_unit( ++ BusWaitForUnits *d, ++ const char *unit, ++ BusWaitForUnitsFlags flags, ++ bus_wait_for_units_unit_callback callback, ++ void *userdata) { ++ ++ _cleanup_(wait_for_item_freep) WaitForItem *item = NULL; ++ int r; ++ ++ assert(d); ++ assert(unit); ++ ++ assert(flags != 0); ++ ++ r = hashmap_ensure_allocated(&d->items, &string_hash_ops); ++ if (r < 0) ++ return r; ++ ++ item = new(WaitForItem, 1); ++ if (!item) ++ return -ENOMEM; ++ ++ *item = (WaitForItem) { ++ .flags = flags, ++ .bus_path = unit_dbus_path_from_name(unit), ++ .unit_callback = callback, ++ .userdata = userdata, ++ .job_id = UINT32_MAX, ++ }; ++ ++ if (!item->bus_path) ++ return -ENOMEM; ++ ++ if (!FLAGS_SET(item->flags, BUS_WAIT_REFFED)) { ++ r = sd_bus_call_method_async( ++ d->bus, ++ NULL, ++ "org.freedesktop.systemd1", ++ item->bus_path, ++ "org.freedesktop.systemd1.Unit", ++ "Ref", ++ NULL, ++ NULL, ++ NULL); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to add reference to unit %s: %m", unit); ++ ++ ++ item->flags |= BUS_WAIT_REFFED; ++ } ++ ++ r = sd_bus_match_signal_async( ++ d->bus, ++ &item->slot_properties_changed, ++ "org.freedesktop.systemd1", ++ item->bus_path, ++ "org.freedesktop.DBus.Properties", ++ "PropertiesChanged", ++ on_properties_changed, ++ NULL, ++ item); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to request match for PropertiesChanged signal: %m"); ++ ++ r = sd_bus_call_method_async( ++ d->bus, ++ &item->slot_get_all, ++ "org.freedesktop.systemd1", ++ item->bus_path, ++ "org.freedesktop.DBus.Properties", ++ "GetAll", ++ on_get_all_properties, ++ item, ++ "s", FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END) ? NULL : "org.freedesktop.systemd1.Unit"); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to request properties of unit %s: %m", unit); ++ ++ r = hashmap_put(d->items, item->bus_path, item); ++ if (r < 0) ++ return r; ++ ++ d->state = BUS_WAIT_RUNNING; ++ item->parent = d; ++ TAKE_PTR(item); ++ return 0; ++} ++ ++int bus_wait_for_units_run(BusWaitForUnits *d) { ++ int r; ++ ++ assert(d); ++ ++ while (d->state == BUS_WAIT_RUNNING) { ++ ++ r = sd_bus_process(d->bus, NULL); ++ if (r < 0) ++ return r; ++ if (r > 0) ++ continue; ++ ++ r = sd_bus_wait(d->bus, (uint64_t) -1); ++ if (r < 0) ++ return r; ++ } ++ ++ return d->state; ++} ++ ++BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d) { ++ assert(d); ++ ++ return d->state; ++} +diff --git a/src/shared/bus-wait-for-units.h b/src/shared/bus-wait-for-units.h +new file mode 100644 +index 0000000000..a20f3d8fd7 +--- /dev/null ++++ b/src/shared/bus-wait-for-units.h +@@ -0,0 +1,35 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++#pragma once ++ ++#include "macro.h" ++#include "sd-bus.h" ++ ++typedef struct BusWaitForUnits BusWaitForUnits; ++ ++typedef enum BusWaitForUnitsState { ++ BUS_WAIT_SUCCESS, /* Nothing to wait for anymore and nothing failed */ ++ BUS_WAIT_FAILURE, /* dito, but something failed */ ++ BUS_WAIT_RUNNING, /* Still something to wait for */ ++ _BUS_WAIT_FOR_UNITS_STATE_MAX, ++ _BUS_WAIT_FOR_UNITS_STATE_INVALID = -1, ++} BusWaitForUnitsState; ++ ++typedef enum BusWaitForUnitsFlags { ++ BUS_WAIT_FOR_MAINTENANCE_END = 1 << 0, /* Wait until the unit is no longer in maintenance state */ ++ BUS_WAIT_FOR_INACTIVE = 1 << 1, /* Wait until the unit is back in inactive or dead state */ ++ BUS_WAIT_NO_JOB = 1 << 2, /* Wait until there's no more job pending */ ++ BUS_WAIT_REFFED = 1 << 3, /* The unit is already reffed with RefUnit() */ ++} BusWaitForUnitsFlags; ++ ++typedef void (*bus_wait_for_units_ready_callback)(BusWaitForUnits *d, BusWaitForUnitsState state, void *userdata); ++typedef void (*bus_wait_for_units_unit_callback)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata); ++ ++int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret); ++BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d); ++ ++BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d); ++void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata); ++int bus_wait_for_units_add_unit(BusWaitForUnits *d, const char *unit, BusWaitForUnitsFlags flags, bus_wait_for_units_unit_callback callback, void *userdata); ++int bus_wait_for_units_run(BusWaitForUnits *d); ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free); +diff --git a/src/shared/meson.build b/src/shared/meson.build +index 54e77e9af6..d0a1bba4c6 100644 +--- a/src/shared/meson.build ++++ b/src/shared/meson.build +@@ -18,6 +18,8 @@ shared_sources = files(''' + bus-unit-util.h + bus-util.c + bus-util.h ++ bus-wait-for-units.c ++ bus-wait-for-units.h + cgroup-show.c + cgroup-show.h + clean-ipc.c diff --git a/SOURCES/0421-shared-fix-assert-call.patch b/SOURCES/0421-shared-fix-assert-call.patch new file mode 100644 index 0000000..5650d80 --- /dev/null +++ b/SOURCES/0421-shared-fix-assert-call.patch @@ -0,0 +1,27 @@ +From 63b5df7c9fda4f7d44674076da5fc5cef4564f3a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 17 Jul 2019 09:39:39 +0200 +Subject: [PATCH] shared: fix assert call + +Fixup for 3572d3df8f8. Coverity CID#1403013. + +(cherry picked from commit 60b17d6fcd988c9995b7d1476d3aba1c4cbbfddd) + +Related: #1830861 +--- + src/shared/bus-wait-for-units.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c +index d07f491e93..de15da1620 100644 +--- a/src/shared/bus-wait-for-units.c ++++ b/src/shared/bus-wait-for-units.c +@@ -190,7 +190,7 @@ static void wait_for_item_check_ready(WaitForItem *item) { + BusWaitForUnits *d; + + assert(item); +- assert(d = item->parent); ++ assert_se(d = item->parent); + + if (FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END)) { + diff --git a/SOURCES/0422-shared-Don-t-try-calling-NULL-callback-in-bus_wait_f.patch b/SOURCES/0422-shared-Don-t-try-calling-NULL-callback-in-bus_wait_f.patch new file mode 100644 index 0000000..43aaa66 --- /dev/null +++ b/SOURCES/0422-shared-Don-t-try-calling-NULL-callback-in-bus_wait_f.patch @@ -0,0 +1,28 @@ +From e607286e070675498fcd5a7ab73bc3da533f9eea Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Wed, 22 Apr 2020 09:51:53 +0200 +Subject: [PATCH] shared: Don't try calling NULL callback in + bus_wait_for_units_clear + +BugLink: https://bugs.launchpad.net/bugs/1870930 +(cherry picked from commit 9f656373082cb13542b877b4f5cb917ef5ff329c) + +Related: #1830861 +--- + src/shared/bus-wait-for-units.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c +index de15da1620..63ba3fd422 100644 +--- a/src/shared/bus-wait-for-units.c ++++ b/src/shared/bus-wait-for-units.c +@@ -91,7 +91,8 @@ static void bus_wait_for_units_clear(BusWaitForUnits *d) { + while ((item = hashmap_first(d->items))) { + d->current = item; + +- item->unit_callback(d, item->bus_path, false, item->userdata); ++ if (item->unit_callback) ++ item->unit_callback(d, item->bus_path, false, item->userdata); + wait_for_item_free(item); + } + diff --git a/SOURCES/0423-shared-add-NULL-callback-check-in-one-more-place.patch b/SOURCES/0423-shared-add-NULL-callback-check-in-one-more-place.patch new file mode 100644 index 0000000..f0d4a5d --- /dev/null +++ b/SOURCES/0423-shared-add-NULL-callback-check-in-one-more-place.patch @@ -0,0 +1,77 @@ +From 48ab0db62ab23307e9f35a862a9c9b33ba3ef261 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 23 Apr 2020 14:53:54 +0200 +Subject: [PATCH] shared: add NULL callback check in one more place + +Follow-up for 9f65637308. + +(cherry picked from commit d3d53e5cd143bf96d1eb0e254f16fa8d458d38ce) + +Related: #1830861 +--- + src/shared/bus-wait-for-units.c | 31 +++++++++++++------------------ + 1 file changed, 13 insertions(+), 18 deletions(-) + +diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c +index 63ba3fd422..ba97922764 100644 +--- a/src/shared/bus-wait-for-units.c ++++ b/src/shared/bus-wait-for-units.c +@@ -80,6 +80,15 @@ static WaitForItem *wait_for_item_free(WaitForItem *item) { + + DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free); + ++static void call_unit_callback_and_wait(BusWaitForUnits *d, WaitForItem *item, bool good) { ++ d->current = item; ++ ++ if (item->unit_callback) ++ item->unit_callback(d, item->bus_path, good, item->userdata); ++ ++ wait_for_item_free(item); ++} ++ + static void bus_wait_for_units_clear(BusWaitForUnits *d) { + WaitForItem *item; + +@@ -88,13 +97,8 @@ static void bus_wait_for_units_clear(BusWaitForUnits *d) { + d->slot_disconnected = sd_bus_slot_unref(d->slot_disconnected); + d->bus = sd_bus_unref(d->bus); + +- while ((item = hashmap_first(d->items))) { +- d->current = item; +- +- if (item->unit_callback) +- item->unit_callback(d, item->bus_path, false, item->userdata); +- wait_for_item_free(item); +- } ++ while ((item = hashmap_first(d->items))) ++ call_unit_callback_and_wait(d, item, false); + + d->items = hashmap_free(d->items); + } +@@ -213,13 +217,7 @@ static void wait_for_item_check_ready(WaitForItem *item) { + return; + } + +- if (item->unit_callback) { +- d->current = item; +- item->unit_callback(d, item->bus_path, true, item->userdata); +- } +- +- wait_for_item_free(item); +- ++ call_unit_callback_and_wait(d, item, true); + bus_wait_for_units_check_ready(d); + } + +@@ -304,10 +302,7 @@ static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error + log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s", + item->bus_path, error->message); + +- d->current = item; +- item->unit_callback(d, item->bus_path, false, item->userdata); +- wait_for_item_free(item); +- ++ call_unit_callback_and_wait(d, item, false); + bus_wait_for_units_check_ready(d); + return 0; + } diff --git a/SOURCES/0424-core-introduce-support-for-cgroup-freezer.patch b/SOURCES/0424-core-introduce-support-for-cgroup-freezer.patch new file mode 100644 index 0000000..58cd2e7 --- /dev/null +++ b/SOURCES/0424-core-introduce-support-for-cgroup-freezer.patch @@ -0,0 +1,1146 @@ +From 046ea98539eb8d7cef93d8062035fa6d9f58efea Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Wed, 29 Apr 2020 17:53:43 +0200 +Subject: [PATCH] core: introduce support for cgroup freezer + +With cgroup v2 the cgroup freezer is implemented as a cgroup +attribute called cgroup.freeze. cgroup can be frozen by writing "1" +to the file and kernel will send us a notification through +"cgroup.events" after the operation is finished and processes in the +cgroup entered quiescent state, i.e. they are not scheduled to +run. Writing "0" to the attribute file does the inverse and process +execution is resumed. + +This commit exposes above low-level functionality through systemd's DBus +API. Each unit type must provide specialized implementation for these +methods, otherwise, we return an error. So far only service, scope, and +slice unit types provide the support. It is possible to check if a +given unit has the support using CanFreeze() DBus property. + +Note that DBus API has a synchronous behavior and we dispatch the reply +to freeze/thaw requests only after the kernel has notified us that +requested operation was completed. + +(cherry picked from commit d9e45bc3abb8adf5a1cb20816ba8f2d2aa65b17e) + +Resolves: #1830861 +--- + man/systemctl.xml | 24 +++++ + src/basic/cgroup-util.c | 18 +++- + src/basic/cgroup-util.h | 1 + + src/basic/unit-def.c | 9 ++ + src/basic/unit-def.h | 12 +++ + src/core/cgroup.c | 105 ++++++++++++++++-- + src/core/cgroup.h | 12 +++ + src/core/dbus-manager.c | 50 +++++++++ + src/core/dbus-unit.c | 96 +++++++++++++++++ + src/core/dbus-unit.h | 3 + + src/core/dbus.c | 7 +- + src/core/scope.c | 3 + + src/core/service.c | 3 + + src/core/slice.c | 80 ++++++++++++++ + src/core/unit.c | 123 ++++++++++++++++++++++ + src/core/unit.h | 20 ++++ + src/libsystemd/sd-bus/bus-common-errors.h | 2 + + src/systemctl/systemctl.c | 105 +++++++++++++++++- + 18 files changed, 660 insertions(+), 13 deletions(-) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index d95d3726af..6145486123 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -873,6 +873,30 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago + the signal to send. + + ++ ++ freeze PATTERN ++ ++ ++ Freeze one or more units specified on the ++ command line using cgroup freezer ++ ++ Freezing the unit will cause all processes contained within the cgroup corresponding to the unit ++ to be suspended. Being suspended means that unit's processes won't be scheduled to run on CPU until thawed. ++ Note that this command is supported only on systems that use unified cgroup hierarchy. Unit is automatically ++ thawed just before we execute a job against the unit, e.g. before the unit is stopped. ++ ++ ++ ++ thaw PATTERN ++ ++ ++ Thaw (unfreeze) one or more units specified on the ++ command line. ++ ++ This is the inverse operation to the freeze command and resumes the execution of ++ processes in the unit's cgroup. ++ ++ + + is-active PATTERN + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index 4c0b73850d..992b12811a 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -137,6 +137,17 @@ bool cg_ns_supported(void) { + return enabled; + } + ++bool cg_freezer_supported(void) { ++ static thread_local int supported = -1; ++ ++ if (supported >= 0) ++ return supported; ++ ++ supported = cg_all_unified() > 0 && access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) == 0; ++ ++ return supported; ++} ++ + int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { + _cleanup_free_ char *fs = NULL; + int r; +@@ -2039,7 +2050,8 @@ int cg_get_keyed_attribute_full( + * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of + * entries as 'keys'. On success each entry will be set to the value of the matching key. + * +- * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */ ++ * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. If mode ++ * is set to GG_KEY_MODE_GRACEFUL we ignore missing keys and return those that were parsed successfully. */ + + r = cg_get_path(controller, path, attribute, &filename); + if (r < 0) +@@ -2089,8 +2101,8 @@ int cg_get_keyed_attribute_full( + + if (mode & CG_KEY_MODE_GRACEFUL) + goto done; +- else +- r = -ENXIO; ++ ++ r = -ENXIO; + + fail: + for (i = 0; i < n; i++) +diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h +index 0673f4b4ce..1210b38a83 100644 +--- a/src/basic/cgroup-util.h ++++ b/src/basic/cgroup-util.h +@@ -250,6 +250,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret); + int cg_kernel_controllers(Set **controllers); + + bool cg_ns_supported(void); ++bool cg_freezer_supported(void); + + int cg_all_unified(void); + int cg_hybrid_unified(void); +diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c +index 46593f6e65..e79cc73dd3 100644 +--- a/src/basic/unit-def.c ++++ b/src/basic/unit-def.c +@@ -107,6 +107,15 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { + + DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); + ++static const char* const freezer_state_table[_FREEZER_STATE_MAX] = { ++ [FREEZER_RUNNING] = "running", ++ [FREEZER_FREEZING] = "freezing", ++ [FREEZER_FROZEN] = "frozen", ++ [FREEZER_THAWING] = "thawing", ++}; ++ ++DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState); ++ + static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { + [AUTOMOUNT_DEAD] = "dead", + [AUTOMOUNT_WAITING] = "waiting", +diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h +index db397a31ed..8eea379a6d 100644 +--- a/src/basic/unit-def.h ++++ b/src/basic/unit-def.h +@@ -44,6 +44,15 @@ typedef enum UnitActiveState { + _UNIT_ACTIVE_STATE_INVALID = -1 + } UnitActiveState; + ++typedef enum FreezerState { ++ FREEZER_RUNNING, ++ FREEZER_FREEZING, ++ FREEZER_FROZEN, ++ FREEZER_THAWING, ++ _FREEZER_STATE_MAX, ++ _FREEZER_STATE_INVALID = -1 ++} FreezerState; ++ + typedef enum AutomountState { + AUTOMOUNT_DEAD, + AUTOMOUNT_WAITING, +@@ -245,6 +254,9 @@ UnitLoadState unit_load_state_from_string(const char *s) _pure_; + const char *unit_active_state_to_string(UnitActiveState i) _const_; + UnitActiveState unit_active_state_from_string(const char *s) _pure_; + ++const char *freezer_state_to_string(FreezerState i) _const_; ++FreezerState freezer_state_from_string(const char *s) _pure_; ++ + const char* automount_state_to_string(AutomountState i) _const_; + AutomountState automount_state_from_string(const char *s) _pure_; + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 7a9857adad..e7ae9273a6 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -2279,6 +2279,51 @@ void unit_add_to_cgroup_empty_queue(Unit *u) { + log_debug_errno(r, "Failed to enable cgroup empty event source: %m"); + } + ++static void unit_remove_from_cgroup_empty_queue(Unit *u) { ++ assert(u); ++ ++ if (!u->in_cgroup_empty_queue) ++ return; ++ ++ LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u); ++ u->in_cgroup_empty_queue = false; ++} ++ ++static int unit_check_cgroup_events(Unit *u) { ++ char *values[2] = {}; ++ int r; ++ ++ assert(u); ++ ++ r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", ++ STRV_MAKE("populated", "frozen"), values); ++ if (r < 0) ++ return r; ++ ++ /* The cgroup.events notifications can be merged together so act as we saw the given state for the ++ * first time. The functions we call to handle given state are idempotent, which makes them ++ * effectively remember the previous state. */ ++ if (values[0]) { ++ if (streq(values[0], "1")) ++ unit_remove_from_cgroup_empty_queue(u); ++ else ++ unit_add_to_cgroup_empty_queue(u); ++ } ++ ++ /* Disregard freezer state changes due to operations not initiated by us */ ++ if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) { ++ if (streq(values[1], "0")) ++ unit_thawed(u); ++ else ++ unit_frozen(u); ++ } ++ ++ free(values[0]); ++ free(values[1]); ++ ++ return 0; ++} ++ + static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + +@@ -2310,15 +2355,12 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, + /* The watch was just removed */ + continue; + +- u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd)); +- if (!u) /* Not that inotify might deliver +- * events for a watch even after it +- * was removed, because it was queued +- * before the removal. Let's ignore +- * this here safely. */ +- continue; ++ /* Note that inotify might deliver events for a watch even after it was removed, ++ * because it was queued before the removal. Let's ignore this here safely. */ + +- unit_add_to_cgroup_empty_queue(u); ++ u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd)); ++ if (u) ++ unit_check_cgroup_events(u); + } + } + } +@@ -2884,6 +2926,46 @@ void manager_invalidate_startup_units(Manager *m) { + unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_BLKIO); + } + ++int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { ++ _cleanup_free_ char *path = NULL; ++ FreezerState target, kernel = _FREEZER_STATE_INVALID; ++ int r; ++ ++ assert(u); ++ assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); ++ ++ if (!u->cgroup_realized) ++ return -EBUSY; ++ ++ target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING; ++ ++ r = unit_freezer_state_kernel(u, &kernel); ++ if (r < 0) ++ log_unit_debug_errno(u, r, "Failed to obtain cgroup freezer state: %m"); ++ ++ if (target == kernel) { ++ u->freezer_state = target; ++ return 0; ++ } ++ ++ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path); ++ if (r < 0) ++ return r; ++ ++ log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing"); ++ ++ if (action == FREEZER_FREEZE) ++ u->freezer_state = FREEZER_FREEZING; ++ else ++ u->freezer_state = FREEZER_THAWING; ++ ++ r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER); ++ if (r < 0) ++ return r; ++ ++ return 0; ++} ++ + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { + [CGROUP_AUTO] = "auto", + [CGROUP_CLOSED] = "closed", +@@ -2919,3 +3001,10 @@ int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) { + } + + DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy); ++ ++static const char* const freezer_action_table[_FREEZER_ACTION_MAX] = { ++ [FREEZER_FREEZE] = "freeze", ++ [FREEZER_THAW] = "thaw", ++}; ++ ++DEFINE_STRING_TABLE_LOOKUP(freezer_action, FreezerAction); +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index 976224336d..36ea77fdc5 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -33,6 +33,14 @@ typedef enum CGroupDevicePolicy { + _CGROUP_DEVICE_POLICY_INVALID = -1 + } CGroupDevicePolicy; + ++typedef enum FreezerAction { ++ FREEZER_FREEZE, ++ FREEZER_THAW, ++ ++ _FREEZER_ACTION_MAX, ++ _FREEZER_ACTION_INVALID = -1, ++} FreezerAction; ++ + struct CGroupDeviceAllow { + LIST_FIELDS(CGroupDeviceAllow, device_allow); + char *path; +@@ -235,3 +243,7 @@ CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; + + bool unit_cgroup_delegate(Unit *u); + int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name); ++int unit_cgroup_freezer_action(Unit *u, FreezerAction action); ++ ++const char* freezer_action_to_string(FreezerAction a) _const_; ++FreezerAction freezer_action_from_string(const char *s) _pure_; +diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c +index b3c011b0df..a0777f63d5 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -683,6 +683,54 @@ static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_err + return bus_unit_method_unref(message, u, error); + } + ++static int method_freeze_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_freeze(message, u, error); ++} ++ ++static int method_thaw_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_thaw(message, u, error); ++} ++ + static int reply_unit_info(sd_bus_message *reply, Unit *u) { + _cleanup_free_ char *unit_path = NULL, *job_path = NULL; + Unit *following; +@@ -2500,6 +2548,8 @@ const sd_bus_vtable bus_manager_vtable[] = { + 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("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("FreezeUnit", "s", NULL, method_freeze_unit, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("ThawUnit", "s", NULL, method_thaw_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED), +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index aa15e47754..ce81103e92 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -42,12 +42,14 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); + static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description); + static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string); ++static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string); + static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string); + static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string); + static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload); + static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual); + static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual); + static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual); ++static BUS_DEFINE_PROPERTY_GET(property_get_can_freeze, "b", Unit, unit_can_freeze); + static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload); + static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0); + +@@ -561,6 +563,79 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error + return sd_bus_reply_method_return(message, NULL); + } + ++static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userdata, sd_bus_error *error, FreezerAction action) { ++ const char* perm; ++ int (*method)(Unit*); ++ Unit *u = userdata; ++ bool reply_no_delay = false; ++ int r; ++ ++ assert(message); ++ assert(u); ++ assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); ++ ++ if (action == FREEZER_FREEZE) { ++ perm = "stop"; ++ method = unit_freeze; ++ } else { ++ perm = "start"; ++ method = unit_thaw; ++ } ++ ++ r = mac_selinux_unit_access_check(u, message, perm, error); ++ if (r < 0) ++ return r; ++ ++ r = bus_verify_manage_units_async_full( ++ u, ++ perm, ++ CAP_SYS_ADMIN, ++ N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."), ++ true, ++ message, ++ error); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ ++ ++ r = method(u); ++ if (r == -EOPNOTSUPP) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id); ++ if (r == -EBUSY) ++ return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job."); ++ if (r == -EHOSTDOWN) ++ return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive."); ++ if (r == -EALREADY) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ reply_no_delay = true; ++ ++ assert(!u->pending_freezer_message); ++ ++ r = sd_bus_message_new_method_return(message, &u->pending_freezer_message); ++ if (r < 0) ++ return r; ++ ++ if (reply_no_delay) { ++ r = bus_unit_send_pending_freezer_message(u); ++ if (r < 0) ++ return r; ++ } ++ ++ return 1; ++} ++ ++int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_THAW); ++} ++ ++int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_FREEZE); ++} ++ + const sd_bus_vtable bus_unit_vtable[] = { + SD_BUS_VTABLE_START(0), + +@@ -592,6 +667,7 @@ const sd_bus_vtable bus_unit_vtable[] = { + SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), ++ SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -607,6 +683,7 @@ const sd_bus_vtable bus_unit_vtable[] = { + SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -650,6 +727,8 @@ const sd_bus_vtable bus_unit_vtable[] = { + SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("Freeze", NULL, NULL, bus_unit_method_freeze, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("Thaw", NULL, NULL, bus_unit_method_thaw, SD_BUS_VTABLE_UNPRIVILEGED), + + /* For dependency types we don't support anymore always return an empty array */ + SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN), +@@ -1209,6 +1288,23 @@ void bus_unit_send_change_signal(Unit *u) { + u->sent_dbus_new_signal = true; + } + ++int bus_unit_send_pending_freezer_message(Unit *u) { ++ int r; ++ ++ assert(u); ++ ++ if (!u->pending_freezer_message) ++ return 0; ++ ++ r = sd_bus_send(NULL, u->pending_freezer_message, NULL); ++ if (r < 0) ++ log_warning_errno(r, "Failed to send queued message, ignoring: %m"); ++ ++ u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message); ++ ++ return 0; ++} ++ + static int send_removed_signal(sd_bus *bus, void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_free_ char *p = NULL; +diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h +index 68eb621836..39aa1bb53c 100644 +--- a/src/core/dbus-unit.h ++++ b/src/core/dbus-unit.h +@@ -11,6 +11,7 @@ extern const sd_bus_vtable bus_unit_vtable[]; + extern const sd_bus_vtable bus_unit_cgroup_vtable[]; + + void bus_unit_send_change_signal(Unit *u); ++int bus_unit_send_pending_freezer_message(Unit *u); + void bus_unit_send_removed_signal(Unit *u); + + int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); +@@ -23,6 +24,8 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu + int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error); + int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error); + int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error); ++int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error); ++int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error); + + int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); + int bus_unit_validate_load_state(Unit *u, sd_bus_error *error); +diff --git a/src/core/dbus.c b/src/core/dbus.c +index 346a440c5d..b69c11c519 100644 +--- a/src/core/dbus.c ++++ b/src/core/dbus.c +@@ -1073,10 +1073,15 @@ static void destroy_bus(Manager *m, sd_bus **bus) { + if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus) + j->bus_track = sd_bus_track_unref(j->bus_track); + +- HASHMAP_FOREACH(u, m->units, i) ++ HASHMAP_FOREACH(u, m->units, i) { + if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus) + u->bus_track = sd_bus_track_unref(u->bus_track); + ++ /* Get rid of pending freezer messages on this bus */ ++ if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus) ++ u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message); ++ } ++ + /* Get rid of queued message on this bus */ + if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus) + m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message); +diff --git a/src/core/scope.c b/src/core/scope.c +index a1a5363244..5a595c65a6 100644 +--- a/src/core/scope.c ++++ b/src/core/scope.c +@@ -601,6 +601,9 @@ const UnitVTable scope_vtable = { + + .kill = scope_kill, + ++ .freeze = unit_freeze_vtable_common, ++ .thaw = unit_thaw_vtable_common, ++ + .get_timeout = scope_get_timeout, + + .serialize = scope_serialize, +diff --git a/src/core/service.c b/src/core/service.c +index 1d98ee37fd..89b41f6783 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -4143,6 +4143,9 @@ const UnitVTable service_vtable = { + + .kill = service_kill, + ++ .freeze = unit_freeze_vtable_common, ++ .thaw = unit_thaw_vtable_common, ++ + .serialize = service_serialize, + .deserialize_item = service_deserialize_item, + +diff --git a/src/core/slice.c b/src/core/slice.c +index 58f18a4dad..b5eb2f5c01 100644 +--- a/src/core/slice.c ++++ b/src/core/slice.c +@@ -344,6 +344,82 @@ static void slice_enumerate_perpetual(Manager *m) { + (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL); + } + ++static bool slice_freezer_action_supported_by_children(Unit *s) { ++ Unit *member; ++ void *v; ++ Iterator i; ++ ++ assert(s); ++ ++ HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) { ++ int r; ++ ++ if (UNIT_DEREF(member->slice) != s) ++ continue; ++ ++ if (member->type == UNIT_SLICE) { ++ r = slice_freezer_action_supported_by_children(member); ++ if (!r) ++ return r; ++ } ++ ++ if (!UNIT_VTABLE(member)->freeze) ++ return false; ++ } ++ ++ return true; ++} ++ ++static int slice_freezer_action(Unit *s, FreezerAction action) { ++ Unit *member; ++ void *v; ++ Iterator i; ++ int r; ++ ++ assert(s); ++ assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); ++ ++ if (!slice_freezer_action_supported_by_children(s)) ++ return log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice"); ++ ++ HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) { ++ if (UNIT_DEREF(member->slice) != s) ++ continue; ++ ++ if (action == FREEZER_FREEZE) ++ r = UNIT_VTABLE(member)->freeze(member); ++ else ++ r = UNIT_VTABLE(member)->thaw(member); ++ ++ if (r < 0) ++ return r; ++ } ++ ++ r = unit_cgroup_freezer_action(s, action); ++ if (r < 0) ++ return r; ++ ++ return 0; ++} ++ ++static int slice_freeze(Unit *s) { ++ assert(s); ++ ++ return slice_freezer_action(s, FREEZER_FREEZE); ++} ++ ++static int slice_thaw(Unit *s) { ++ assert(s); ++ ++ return slice_freezer_action(s, FREEZER_THAW); ++} ++ ++static bool slice_can_freeze(Unit *s) { ++ assert(s); ++ ++ return slice_freezer_action_supported_by_children(s); ++} ++ + const UnitVTable slice_vtable = { + .object_size = sizeof(Slice), + .cgroup_context_offset = offsetof(Slice, cgroup_context), +@@ -368,6 +444,10 @@ const UnitVTable slice_vtable = { + + .kill = slice_kill, + ++ .freeze = slice_freeze, ++ .thaw = slice_thaw, ++ .can_freeze = slice_can_freeze, ++ + .serialize = slice_serialize, + .deserialize_item = slice_deserialize_item, + +diff --git a/src/core/unit.c b/src/core/unit.c +index f57260727f..29ce6c1fb7 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -583,6 +583,7 @@ void unit_free(Unit *u) { + sd_bus_slot_unref(u->match_bus_slot); + sd_bus_track_unref(u->bus_track); + u->deserialized_refs = strv_free(u->deserialized_refs); ++ u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message); + + unit_free_requires_mounts_for(u); + +@@ -685,6 +686,38 @@ void unit_free(Unit *u) { + free(u); + } + ++FreezerState unit_freezer_state(Unit *u) { ++ assert(u); ++ ++ return u->freezer_state; ++} ++ ++int unit_freezer_state_kernel(Unit *u, FreezerState *ret) { ++ char *values[1] = {}; ++ int r; ++ ++ assert(u); ++ ++ r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", ++ STRV_MAKE("frozen"), values); ++ if (r < 0) ++ return r; ++ ++ r = _FREEZER_STATE_INVALID; ++ ++ if (values[0]) { ++ if (streq(values[0], "0")) ++ r = FREEZER_RUNNING; ++ else if (streq(values[0], "1")) ++ r = FREEZER_FROZEN; ++ } ++ ++ free(values[0]); ++ *ret = r; ++ ++ return 0; ++} ++ + UnitActiveState unit_active_state(Unit *u) { + assert(u); + +@@ -1760,6 +1793,7 @@ int unit_start(Unit *u) { + * before it will start again. */ + + unit_add_to_dbus_queue(u); ++ unit_cgroup_freezer_action(u, FREEZER_THAW); + + return UNIT_VTABLE(u)->start(u); + } +@@ -1812,6 +1846,7 @@ int unit_stop(Unit *u) { + return -EBADR; + + unit_add_to_dbus_queue(u); ++ unit_cgroup_freezer_action(u, FREEZER_THAW); + + return UNIT_VTABLE(u)->stop(u); + } +@@ -1868,6 +1903,8 @@ int unit_reload(Unit *u) { + return 0; + } + ++ unit_cgroup_freezer_action(u, FREEZER_THAW); ++ + return UNIT_VTABLE(u)->reload(u); + } + +@@ -3208,6 +3245,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { + if (!sd_id128_is_null(u->invocation_id)) + unit_serialize_item_format(u, f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id)); + ++ (void) unit_serialize_item_format(u, f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u))); ++ + bus_track_serialize(u->bus_track, f, "ref"); + + for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) { +@@ -3574,6 +3613,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { + log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m"); + } + ++ continue; ++ } else if (streq(l, "freezer-state")) { ++ FreezerState s; ++ ++ s = freezer_state_from_string(v); ++ if (s < 0) ++ log_unit_debug(u, "Failed to deserialize freezer-state '%s', ignoring.", v); ++ else ++ u->freezer_state = s; ++ + continue; + } + +@@ -5507,6 +5556,80 @@ void unit_log_skip(Unit *u, const char *result) { + "UNIT_RESULT=%s", result); + } + ++bool unit_can_freeze(Unit *u) { ++ assert(u); ++ ++ if (UNIT_VTABLE(u)->can_freeze) ++ return UNIT_VTABLE(u)->can_freeze(u); ++ ++ return UNIT_VTABLE(u)->freeze; ++} ++ ++void unit_frozen(Unit *u) { ++ assert(u); ++ ++ u->freezer_state = FREEZER_FROZEN; ++ ++ bus_unit_send_pending_freezer_message(u); ++} ++ ++void unit_thawed(Unit *u) { ++ assert(u); ++ ++ u->freezer_state = FREEZER_RUNNING; ++ ++ bus_unit_send_pending_freezer_message(u); ++} ++ ++static int unit_freezer_action(Unit *u, FreezerAction action) { ++ UnitActiveState s; ++ int (*method)(Unit*); ++ int r; ++ ++ assert(u); ++ assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); ++ ++ method = action == FREEZER_FREEZE ? UNIT_VTABLE(u)->freeze : UNIT_VTABLE(u)->thaw; ++ if (!method || !cg_freezer_supported()) ++ return -EOPNOTSUPP; ++ ++ if (u->job) ++ return -EBUSY; ++ ++ if (u->load_state != UNIT_LOADED) ++ return -EHOSTDOWN; ++ ++ s = unit_active_state(u); ++ if (s != UNIT_ACTIVE) ++ return -EHOSTDOWN; ++ ++ if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) ++ return -EALREADY; ++ ++ r = method(u); ++ if (r <= 0) ++ return r; ++ ++ return 1; ++} ++ ++int unit_freeze(Unit *u) { ++ return unit_freezer_action(u, FREEZER_FREEZE); ++} ++ ++int unit_thaw(Unit *u) { ++ return unit_freezer_action(u, FREEZER_THAW); ++} ++ ++/* Wrappers around low-level cgroup freezer operations common for service and scope units */ ++int unit_freeze_vtable_common(Unit *u) { ++ return unit_cgroup_freezer_action(u, FREEZER_FREEZE); ++} ++ ++int unit_thaw_vtable_common(Unit *u) { ++ return unit_cgroup_freezer_action(u, FREEZER_THAW); ++} ++ + static const char* const collect_mode_table[_COLLECT_MODE_MAX] = { + [COLLECT_INACTIVE] = "inactive", + [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed", +diff --git a/src/core/unit.h b/src/core/unit.h +index b40ff9b961..6e37fd6f5a 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -118,6 +118,9 @@ typedef struct Unit { + UnitLoadState load_state; + Unit *merged_into; + ++ FreezerState freezer_state; ++ sd_bus_message *pending_freezer_message; ++ + char *id; /* One name is special because we use it for identification. Points to an entry in the names set */ + char *instance; + +@@ -456,6 +459,11 @@ typedef struct UnitVTable { + + int (*kill)(Unit *u, KillWho w, int signo, sd_bus_error *error); + ++ /* Freeze the unit */ ++ int (*freeze)(Unit *u); ++ int (*thaw)(Unit *u); ++ bool (*can_freeze)(Unit *u); ++ + bool (*can_reload)(Unit *u); + + /* Write all data that cannot be restored from other sources +@@ -641,6 +649,8 @@ const char *unit_description(Unit *u) _pure_; + bool unit_has_name(Unit *u, const char *name); + + UnitActiveState unit_active_state(Unit *u); ++FreezerState unit_freezer_state(Unit *u); ++int unit_freezer_state_kernel(Unit *u, FreezerState *ret); + + const char* unit_sub_state_to_string(Unit *u); + +@@ -814,6 +824,16 @@ void unit_log_failure(Unit *u, const char *result); + * after some execution, rather than succeeded or failed. */ + void unit_log_skip(Unit *u, const char *result); + ++bool unit_can_freeze(Unit *u); ++int unit_freeze(Unit *u); ++void unit_frozen(Unit *u); ++ ++int unit_thaw(Unit *u); ++void unit_thawed(Unit *u); ++ ++int unit_freeze_vtable_common(Unit *u); ++int unit_thaw_vtable_common(Unit *u); ++ + /* Macros which append UNIT= or USER_UNIT= to the message */ + + #define log_unit_full(unit, level, error, ...) \ +diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h +index 3945c7f6ac..77da78d4d4 100644 +--- a/src/libsystemd/sd-bus/bus-common-errors.h ++++ b/src/libsystemd/sd-bus/bus-common-errors.h +@@ -30,6 +30,8 @@ + #define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser" + #define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced" + #define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull" ++#define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy" ++#define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive" + + #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" + #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index e0db97e339..e963f19b0a 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -27,6 +27,7 @@ + #include "bus-message.h" + #include "bus-unit-util.h" + #include "bus-util.h" ++#include "bus-wait-for-units.h" + #include "cgroup-show.h" + #include "cgroup-util.h" + #include "copy.h" +@@ -3728,6 +3729,98 @@ static int kill_unit(int argc, char *argv[], void *userdata) { + + return r; + } ++static int freeze_or_thaw_unit(int argc, char *argv[], void *userdata) { ++ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL; ++ _cleanup_strv_free_ char **names = NULL; ++ int r, ret = EXIT_SUCCESS; ++ char **name; ++ const char *method; ++ sd_bus *bus; ++ ++ r = acquire_bus(BUS_FULL, &bus); ++ if (r < 0) ++ return r; ++ ++ polkit_agent_open_maybe(); ++ ++ r = expand_names(bus, strv_skip(argv, 1), NULL, &names); ++ if (r < 0) ++ return log_error_errno(r, "Failed to expand names: %m"); ++ ++ if (!arg_no_block) { ++ r = bus_wait_for_units_new(bus, &w); ++ if (r < 0) ++ return log_error_errno(r, "Failed to allocate unit waiter: %m"); ++ } ++ ++ if (streq(argv[0], "freeze")) ++ method = "FreezeUnit"; ++ else if (streq(argv[0], "thaw")) ++ method = "ThawUnit"; ++ else ++ assert_not_reached("Unhandled method"); ++ ++ STRV_FOREACH(name, names) { ++ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; ++ ++ if (w) { ++ /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "RefUnit", ++ &error, ++ NULL, ++ "s", *name); ++ if (r < 0) { ++ log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r)); ++ if (ret == EXIT_SUCCESS) ++ ret = r; ++ continue; ++ } ++ } ++ ++ r = sd_bus_message_new_method_call( ++ bus, ++ &m, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ method); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_message_append(m, "s", *name); ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ r = sd_bus_call(bus, m, 0, &error, NULL); ++ if (r < 0) { ++ log_error_errno(r, "Failed to %s unit %s: %s", argv[0], *name, bus_error_message(&error, r)); ++ if (ret == EXIT_SUCCESS) { ++ ret = r; ++ continue; ++ } ++ } ++ ++ if (w) { ++ r = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_REFFED|BUS_WAIT_FOR_MAINTENANCE_END, NULL, NULL); ++ if (r < 0) ++ return log_error_errno(r, "Failed to watch unit %s: %m", *name); ++ } ++ } ++ ++ r = bus_wait_for_units_run(w); ++ if (r < 0) ++ return log_error_errno(r, "Failed to wait for units: %m"); ++ if (r == BUS_WAIT_FAILURE) ++ ret = EXIT_FAILURE; ++ ++ return ret; ++} + + typedef struct ExecStatusInfo { + char *name; +@@ -3832,6 +3925,7 @@ typedef struct UnitStatusInfo { + const char *id; + const char *load_state; + const char *active_state; ++ const char *freezer_state; + const char *sub_state; + const char *unit_file_state; + const char *unit_file_preset; +@@ -3949,7 +4043,7 @@ static void print_status_info( + bool *ellipsized) { + + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX]; +- const char *s1, *s2, *active_on, *active_off, *on, *off, *ss; ++ const char *s1, *s2, *active_on, *active_off, *on, *off, *ss, *fs; + _cleanup_free_ char *formatted_path = NULL; + ExecStatusInfo *p; + usec_t timestamp; +@@ -4056,6 +4150,10 @@ static void print_status_info( + printf(" Active: %s%s%s", + active_on, strna(i->active_state), active_off); + ++ fs = !isempty(i->freezer_state) && !streq(i->freezer_state, "running") ? i->freezer_state : NULL; ++ if (fs) ++ printf(" %s(%s)%s", ansi_highlight_yellow(), fs, active_off); ++ + if (!isempty(i->result) && !streq(i->result, "success")) + printf(" (Result: %s)", i->result); + +@@ -4985,6 +5083,7 @@ static int show_one( + { "Id", "s", NULL, offsetof(UnitStatusInfo, id) }, + { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) }, + { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) }, ++ { "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) }, + { "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) }, + { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) }, + { "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) }, +@@ -7139,6 +7238,8 @@ static void systemctl_help(void) { + " if supported, otherwise restart\n" + " isolate UNIT Start one unit and stop all others\n" + " kill UNIT... Send signal to processes of a unit\n" ++ " freeze PATTERN... Freeze execution of unit processes\n" ++ " thaw PATTERN... Resume execution of a frozen unit\n" + " is-active PATTERN... Check whether units are active\n" + " is-failed PATTERN... Check whether units are failed\n" + " status [PATTERN...|PID...] Show runtime status of one or more units\n" +@@ -8280,6 +8381,8 @@ static int systemctl_main(int argc, char *argv[]) { + { "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */ + { "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit }, + { "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit }, ++ { "freeze", 2, VERB_ANY, VERB_ONLINE_ONLY, freeze_or_thaw_unit }, ++ { "thaw", 2, VERB_ANY, VERB_ONLINE_ONLY, freeze_or_thaw_unit }, + { "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, + { "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */ + { "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed }, diff --git a/SOURCES/0425-core-cgroup-fix-return-value-of-unit_cgorup_freezer_.patch b/SOURCES/0425-core-cgroup-fix-return-value-of-unit_cgorup_freezer_.patch new file mode 100644 index 0000000..f78f7a7 --- /dev/null +++ b/SOURCES/0425-core-cgroup-fix-return-value-of-unit_cgorup_freezer_.patch @@ -0,0 +1,31 @@ +From abf2fb67dc3d7da8db030ea8b8db73a20acc08a9 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 7 May 2020 17:23:30 +0200 +Subject: [PATCH] core/cgroup: fix return value of unit_cgorup_freezer_action() + +We should return 0 only if current freezer state, as reported by the +kernel, is already the desired state. Otherwise, we would dispatch +return dbus message prematurely in bus_unit_method_freezer_generic(). + +Thanks to Frantisek Sumsal for reporting the issue. + +(cherry picked from commit d910f4c2b2542544d7b187a09605da7a0f220837) + +Related: #1830861 +--- + src/core/cgroup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index e7ae9273a6..2d819b8ebb 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -2963,7 +2963,7 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { + if (r < 0) + return r; + +- return 0; ++ return 1; + } + + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { diff --git a/SOURCES/0426-core-fix-the-return-value-in-order-to-make-sure-we-d.patch b/SOURCES/0426-core-fix-the-return-value-in-order-to-make-sure-we-d.patch new file mode 100644 index 0000000..94cccdd --- /dev/null +++ b/SOURCES/0426-core-fix-the-return-value-in-order-to-make-sure-we-d.patch @@ -0,0 +1,31 @@ +From 4da9dfaec0d7d232d8bfed0d7f65afd65369bc8c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 5 Jun 2020 15:23:12 +0200 +Subject: [PATCH] core: fix the return value in order to make sure we don't + dipatch method return too early + +Actually, it is the same kind of problem as in d910f4c . Basically, we +need to return 1 on success code path in slice_freezer_action(). +Otherwise we dispatch DBus return message too soon. + +Fixes: #16050 +(cherry picked from commit 2884836e3c26fa76718319cdc6d13136bbc1354d) + +Related: #1830861 +--- + src/core/slice.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/slice.c b/src/core/slice.c +index b5eb2f5c01..c10e830917 100644 +--- a/src/core/slice.c ++++ b/src/core/slice.c +@@ -399,7 +399,7 @@ static int slice_freezer_action(Unit *s, FreezerAction action) { + if (r < 0) + return r; + +- return 0; ++ return 1; + } + + static int slice_freeze(Unit *s) { diff --git a/SOURCES/0427-test-add-test-for-cgroup-v2-freezer-support.patch b/SOURCES/0427-test-add-test-for-cgroup-v2-freezer-support.patch new file mode 100644 index 0000000..4d68414 --- /dev/null +++ b/SOURCES/0427-test-add-test-for-cgroup-v2-freezer-support.patch @@ -0,0 +1,397 @@ +From 762959047c1e1a5d4b6a6fe85cfa8a14c622e4da Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 3 Apr 2020 09:13:51 +0200 +Subject: [PATCH] test: add test for cgroup v2 freezer support + +(cherry picked from commit d446ae89c0168f17eed7135ac06df3b294b3fcc6) + +Related: #1830861 +--- + ci/travis-centos-rhel8.sh | 2 +- + test/TEST-38-FREEZER/Makefile | 1 + + test/TEST-38-FREEZER/test.sh | 53 ++++++ + test/TEST-38-FREEZER/testsuite.sh | 293 ++++++++++++++++++++++++++++++ + 4 files changed, 348 insertions(+), 1 deletion(-) + create mode 120000 test/TEST-38-FREEZER/Makefile + create mode 100755 test/TEST-38-FREEZER/test.sh + create mode 100755 test/TEST-38-FREEZER/testsuite.sh + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index a1502e15ee..cd0857fd29 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -15,7 +15,7 @@ CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" + DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" + DOCKER_RUN="${DOCKER_RUN:-docker run}" + REPO_ROOT="${REPO_ROOT:-$PWD}" +-ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq) ++ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq diffutils) + # RHEL8 options + CONFIGURE_OPTS=( + -Dsysvinit-path=/etc/rc.d/init.d +diff --git a/test/TEST-38-FREEZER/Makefile b/test/TEST-38-FREEZER/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-38-FREEZER/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-38-FREEZER/test.sh b/test/TEST-38-FREEZER/test.sh +new file mode 100755 +index 0000000000..0d00754b5c +--- /dev/null ++++ b/test/TEST-38-FREEZER/test.sh +@@ -0,0 +1,53 @@ ++#!/bin/bash ++set -e ++TEST_DESCRIPTION="test unit freezing and thawing via DBus and systemctl" ++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) ++ ++ setup_basic_environment ++ dracut_install tr cut timeout ++ ++ # mask some services that we do not want to run in these tests ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service ++ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service < $initdir/etc/systemd/system/testsuite-38-sleep.service << EOF ++[Service] ++ExecStart=/bin/sleep 3600 ++EOF ++ ++ cp testsuite.sh $initdir/ ++ setup_testsuite ++ ) ++ setup_nspawn_root ++ ++ ddebug "umount $TESTDIR/root" ++ umount $TESTDIR/root ++} ++ ++ ++do_test "$@" +diff --git a/test/TEST-38-FREEZER/testsuite.sh b/test/TEST-38-FREEZER/testsuite.sh +new file mode 100755 +index 0000000000..6fcadb8f8e +--- /dev/null ++++ b/test/TEST-38-FREEZER/testsuite.sh +@@ -0,0 +1,293 @@ ++#!/usr/bin/env bash ++ ++set -ex ++set -o pipefail ++ ++systemd-analyze log-level debug ++systemd-analyze log-target console ++ ++unit=testsuite-38-sleep.service ++ ++start_test_service() { ++ systemctl daemon-reload ++ systemctl start "${unit}" ++} ++ ++dbus_freeze() { ++ local suffix= ++ suffix="${1##*.}" ++ ++ local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" ++ local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" ++ ++ busctl call \ ++ org.freedesktop.systemd1 \ ++ "${object_path}" \ ++ org.freedesktop.systemd1.Unit \ ++ Freeze ++} ++ ++dbus_thaw() { ++ local suffix= ++ suffix="${1##*.}" ++ ++ local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" ++ local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" ++ ++ busctl call \ ++ org.freedesktop.systemd1 \ ++ "${object_path}" \ ++ org.freedesktop.systemd1.Unit \ ++ Thaw ++} ++ ++dbus_freeze_unit() { ++ busctl call \ ++ org.freedesktop.systemd1 \ ++ /org/freedesktop/systemd1 \ ++ org.freedesktop.systemd1.Manager \ ++ FreezeUnit \ ++ s \ ++ "$1" ++} ++ ++dbus_thaw_unit() { ++ busctl call \ ++ org.freedesktop.systemd1 \ ++ /org/freedesktop/systemd1 \ ++ org.freedesktop.systemd1.Manager \ ++ ThawUnit \ ++ s \ ++ "$1" ++} ++ ++dbus_can_freeze() { ++ local suffix= ++ suffix="${1##*.}" ++ ++ local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" ++ local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" ++ ++ busctl get-property \ ++ org.freedesktop.systemd1 \ ++ "${object_path}" \ ++ org.freedesktop.systemd1.Unit \ ++ CanFreeze ++} ++ ++check_freezer_state() { ++ local suffix= ++ suffix="${1##*.}" ++ ++ local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)" ++ local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}" ++ ++ state=$(busctl get-property \ ++ org.freedesktop.systemd1 \ ++ "${object_path}" \ ++ org.freedesktop.systemd1.Unit \ ++ FreezerState | cut -d " " -f2 | tr -d '"') ++ ++ [ "$state" = "$2" ] || { ++ echo "error: unexpected freezer state, expected: $2, actual: $state" >&2 ++ exit 1 ++ } ++} ++ ++check_cgroup_state() { ++ grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events ++} ++ ++test_dbus_api() { ++ echo "Test that DBus API works:" ++ echo -n " - Freeze(): " ++ dbus_freeze "${unit}" ++ check_freezer_state "${unit}" "frozen" ++ check_cgroup_state "$unit" 1 ++ echo "[ OK ]" ++ ++ echo -n " - Thaw(): " ++ dbus_thaw "${unit}" ++ check_freezer_state "${unit}" "running" ++ check_cgroup_state "$unit" 0 ++ echo "[ OK ]" ++ ++ echo -n " - FreezeUnit(): " ++ dbus_freeze_unit "${unit}" ++ check_freezer_state "${unit}" "frozen" ++ check_cgroup_state "$unit" 1 ++ echo "[ OK ]" ++ ++ echo -n " - ThawUnit(): " ++ dbus_thaw_unit "${unit}" ++ check_freezer_state "${unit}" "running" ++ check_cgroup_state "$unit" 0 ++ echo "[ OK ]" ++ ++ echo -n " - CanFreeze(): " ++ output=$(dbus_can_freeze "${unit}") ++ [ "$output" = "b true" ] ++ echo "[ OK ]" ++ ++ echo ++} ++ ++test_jobs() { ++ local pid_before= ++ local pid_after= ++ echo "Test that it is possible to apply jobs on frozen units:" ++ ++ systemctl start "${unit}" ++ dbus_freeze "${unit}" ++ check_freezer_state "${unit}" "frozen" ++ ++ echo -n " - restart: " ++ pid_before=$(systemctl show -p MainPID "${unit}" --value) ++ systemctl restart "${unit}" ++ pid_after=$(systemctl show -p MainPID "${unit}" --value) ++ [ "$pid_before" != "$pid_after" ] && echo "[ OK ]" ++ ++ dbus_freeze "${unit}" ++ check_freezer_state "${unit}" "frozen" ++ ++ echo -n " - stop: " ++ timeout 5s systemctl stop "${unit}" ++ echo "[ OK ]" ++ ++ echo ++} ++ ++test_systemctl() { ++ echo "Test that systemctl freeze/thaw verbs:" ++ ++ systemctl start "$unit" ++ ++ echo -n " - freeze: " ++ systemctl freeze "$unit" ++ check_freezer_state "${unit}" "frozen" ++ check_cgroup_state "$unit" 1 ++ # Freezing already frozen unit should be NOP and return quickly ++ timeout 3s systemctl freeze "$unit" ++ echo "[ OK ]" ++ ++ echo -n " - thaw: " ++ systemctl thaw "$unit" ++ check_freezer_state "${unit}" "running" ++ check_cgroup_state "$unit" 0 ++ # Likewise thawing already running unit shouldn't block ++ timeout 3s systemctl thaw "$unit" ++ echo "[ OK ]" ++ ++ systemctl stop "$unit" ++ ++ echo ++} ++ ++test_systemctl_show() { ++ echo "Test systemctl show integration:" ++ ++ systemctl start "$unit" ++ ++ echo -n " - FreezerState property: " ++ state=$(systemctl show -p FreezerState --value "$unit") ++ [ "$state" = "running" ] ++ systemctl freeze "$unit" ++ state=$(systemctl show -p FreezerState --value "$unit") ++ [ "$state" = "frozen" ] ++ systemctl thaw "$unit" ++ echo "[ OK ]" ++ ++ echo -n " - CanFreeze property: " ++ state=$(systemctl show -p CanFreeze --value "$unit") ++ [ "$state" = "yes" ] ++ echo "[ OK ]" ++ ++ systemctl stop "$unit" ++ echo ++} ++ ++test_recursive() { ++ local slice="bar.slice" ++ local unit="baz.service" ++ ++ systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 ++ ++ echo "Test recursive freezing:" ++ ++ echo -n " - freeze: " ++ systemctl freeze "$slice" ++ check_freezer_state "${slice}" "frozen" ++ check_freezer_state "${unit}" "frozen" ++ grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events ++ grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events ++ echo "[ OK ]" ++ ++ echo -n " - thaw: " ++ systemctl thaw "$slice" ++ check_freezer_state "${unit}" "running" ++ check_freezer_state "${slice}" "running" ++ grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events ++ grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events ++ echo "[ OK ]" ++ ++ systemctl stop "$unit" ++ systemctl stop "$slice" ++ ++ echo ++} ++ ++test_preserve_state() { ++ local slice="bar.slice" ++ local unit="baz.service" ++ ++ systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1 ++ ++ echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):" ++ ++ echo -n " - freeze from outside: " ++ echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze ++ ++ # Our state should not be affected ++ check_freezer_state "${slice}" "running" ++ check_freezer_state "${unit}" "running" ++ ++ # However actual kernel state should be frozen ++ grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events ++ grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events ++ echo "[ OK ]" ++ ++ echo -n " - thaw from outside: " ++ echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze ++ check_freezer_state "${unit}" "running" ++ check_freezer_state "${slice}" "running" ++ grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events ++ grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events ++ echo "[ OK ]" ++ ++ echo -n " - thaw from outside while inner service is frozen: " ++ systemctl freeze "$unit" ++ check_freezer_state "${unit}" "frozen" ++ echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze ++ echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze ++ check_freezer_state "${slice}" "running" ++ check_freezer_state "${unit}" "frozen" ++ echo "[ OK ]" ++ ++ systemctl stop "$unit" ++ systemctl stop "$slice" ++ ++ echo ++} ++ ++test -e /sys/fs/cgroup/system.slice/cgroup.freeze && { ++ start_test_service ++ test_dbus_api ++ test_systemctl ++ test_systemctl_show ++ test_jobs ++ test_recursive ++ test_preserve_state ++} ++ ++echo OK > /testok ++exit 0 diff --git a/SOURCES/0428-fix-mis-merge.patch b/SOURCES/0428-fix-mis-merge.patch new file mode 100644 index 0000000..544299a --- /dev/null +++ b/SOURCES/0428-fix-mis-merge.patch @@ -0,0 +1,29 @@ +From 8e1cc941607263a4f8454d0d6d5939aec3be1fcb Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 23 Jun 2020 13:58:21 +0200 +Subject: [PATCH] fix mis-merge + +Resolves: #1848421 +--- + src/core/cgroup.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 2d819b8ebb..e0eb184fd2 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -664,11 +664,13 @@ static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t qu + + if (quota != USEC_INFINITY) { + xsprintf(buf, USEC_FMT "\n", MAX(quota * period / USEC_PER_SEC, USEC_PER_MSEC)); ++ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf); ++ } ++ else + r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1"); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set cpu.cfs_quota_us: %m"); +- } + } + + static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) { diff --git a/SOURCES/0429-tests-sleep-a-bit-and-give-kernel-time-to-perform-th.patch b/SOURCES/0429-tests-sleep-a-bit-and-give-kernel-time-to-perform-th.patch new file mode 100644 index 0000000..82805da --- /dev/null +++ b/SOURCES/0429-tests-sleep-a-bit-and-give-kernel-time-to-perform-th.patch @@ -0,0 +1,36 @@ +From 9028873ee3bffe37b50fa7ada123fcf270de4658 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 5 Jun 2020 11:35:01 +0200 +Subject: [PATCH] tests: sleep a bit and give kernel time to perform the action + after manual freeze/thaw + +Fixes: #16050 +(cherry picked from commit a0d79df8e59c6bb6dc0382d71e835dec869a7df4) + +Related: #1848421 +--- + test/TEST-38-FREEZER/testsuite.sh | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/test/TEST-38-FREEZER/testsuite.sh b/test/TEST-38-FREEZER/testsuite.sh +index 6fcadb8f8e..18b7bd6dce 100755 +--- a/test/TEST-38-FREEZER/testsuite.sh ++++ b/test/TEST-38-FREEZER/testsuite.sh +@@ -246,6 +246,8 @@ test_preserve_state() { + + echo -n " - freeze from outside: " + echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze ++ # Give kernel some time to freeze the slice ++ sleep 1 + + # Our state should not be affected + check_freezer_state "${slice}" "running" +@@ -258,6 +260,8 @@ test_preserve_state() { + + echo -n " - thaw from outside: " + echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze ++ sleep 1 ++ + check_freezer_state "${unit}" "running" + check_freezer_state "${slice}" "running" + grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events diff --git a/SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch b/SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch new file mode 100644 index 0000000..8d202a4 --- /dev/null +++ b/SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch @@ -0,0 +1,26 @@ +From 91dddaafe0b6fcc9c0a57d2feef599b82ce2a146 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 26 Mar 2020 13:34:20 +0100 +Subject: [PATCH] device: make sure we emit PropertiesChanged signal once we + set sysfs + +(cherry picked from commit 7c4d139485139eae95b17a1d54cb51ae958abd70) + +Related: #1793533 +--- + src/core/device.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/core/device.c b/src/core/device.c +index a2d00a0fbe..21fe3802bd 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -81,6 +81,8 @@ static int device_set_sysfs(Device *d, const char *sysfs) { + } + + d->sysfs = TAKE_PTR(copy); ++ unit_add_to_dbus_queue(UNIT(d)); ++ + return 0; + } + diff --git a/SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch b/SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch new file mode 100644 index 0000000..e0986d5 --- /dev/null +++ b/SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch @@ -0,0 +1,44 @@ +From a4cefc9f8bf24b2fdcc62cc0d2685698814374d4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 26 Mar 2020 13:35:11 +0100 +Subject: [PATCH] device: don't emit PropetiesChanged needlessly +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Functions called from device_setup_unit() already make sure that unit is +enqueued in case it is a new unit or properties exported on the bus have +changed. + +This should prevent unnecessary DBus wakeups and associated DBus traffic +when device_setup_unit() was called while reparsing /proc/self/mountinfo +due to the mountinfo notifications. Note that we parse +/proc/self/mountinfo quite often on the busy systems (e.g. k8s container +hosts) but majority of the time mounts didn't change, only some mount +got added. Thus we don't need to generate PropertiesChanged for devices +associated with the mounts that didn't change. + +Thanks to Renaud Métrich for debugging the +problem and providing draft version of the patch. + +(cherry picked from commit 2e129d5d6bd6bd8be4b5359e81a880cbf72a44b8) + +Resolves: #1793533 +--- + src/core/device.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/core/device.c b/src/core/device.c +index 21fe3802bd..021c28dfbd 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -549,9 +549,6 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa + if (dev && device_is_bound_by_mounts(DEVICE(u), dev)) + device_upgrade_mount_deps(u); + +- /* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */ +- unit_add_to_dbus_queue(u); +- + return 0; + + fail: diff --git a/SOURCES/0432-units-add-generic-boot-complete.target.patch b/SOURCES/0432-units-add-generic-boot-complete.target.patch new file mode 100644 index 0000000..912b230 --- /dev/null +++ b/SOURCES/0432-units-add-generic-boot-complete.target.patch @@ -0,0 +1,46 @@ +From dd573e5fbac858c20628052acfa19401d3e0d135 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 22 Jun 2018 12:52:28 +0200 +Subject: [PATCH] units: add generic boot-complete.target + +(cherry picked from commit 329d20db3cb02d789473b8f7e4a59526fcbf5728) + +Resolves: #1872243 +--- + units/boot-complete.target | 14 ++++++++++++++ + units/meson.build | 1 + + 2 files changed, 15 insertions(+) + create mode 100644 units/boot-complete.target + +diff --git a/units/boot-complete.target b/units/boot-complete.target +new file mode 100644 +index 0000000000..f0b9e57e7c +--- /dev/null ++++ b/units/boot-complete.target +@@ -0,0 +1,14 @@ ++# SPDX-License-Identifier: LGPL-2.1+ ++# ++# This file is part of systemd. ++# ++# systemd is free software; you can redistribute it and/or modify it ++# under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation; either version 2.1 of the License, or ++# (at your option) any later version. ++ ++[Unit] ++Description=Boot Completion Check ++Documentation=man:systemd.special(7) ++Requires=sysinit.target ++After=sysinit.target +diff --git a/units/meson.build b/units/meson.build +index e118d81888..a1cd2524dc 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -3,6 +3,7 @@ + units = [ + ['basic.target', ''], + ['bluetooth.target', ''], ++ ['boot-complete.target', ''], + ['cryptsetup-pre.target', 'HAVE_LIBCRYPTSETUP'], + ['cryptsetup.target', 'HAVE_LIBCRYPTSETUP', + 'sysinit.target.wants/'], diff --git a/SOURCES/0433-man-document-new-boot-complete.target-unit.patch b/SOURCES/0433-man-document-new-boot-complete.target-unit.patch new file mode 100644 index 0000000..376958c --- /dev/null +++ b/SOURCES/0433-man-document-new-boot-complete.target-unit.patch @@ -0,0 +1,53 @@ +From 8ad89170001c9aba8849630ddb5da81d9e24a1bc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 25 Jun 2018 17:21:34 +0200 +Subject: [PATCH] man: document new "boot-complete.target" unit + +(cherry picked from commit 82ea38258c0f4964c2f3ad3691c6e4554c4f0bb0) + +Related: #1872243 +--- + man/systemd.special.xml | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/man/systemd.special.xml b/man/systemd.special.xml +index fb12805fff..c9d4345016 100644 +--- a/man/systemd.special.xml ++++ b/man/systemd.special.xml +@@ -29,6 +29,7 @@ + cryptsetup-pre.target, + cryptsetup.target, + ctrl-alt-del.target, ++ boot-complete.target, + default.target, + emergency.target, + exit.target, +@@ -646,6 +647,28 @@ + + + ++ ++ boot-complete.target ++ ++ This target is intended as generic synchronization point for services that shall determine or act on ++ whether the boot process completed successfully. Order units that are required to succeed for a boot process ++ to be considered successful before this unit, and add a Requires= dependency from the ++ target unit to them. Order units that shall only run when the boot process is considered successful after the ++ target unit and pull in the target from it, also with Requires=. Note that by default this ++ target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by ++ units that want to run only on successful boots. ++ ++ See ++ systemd-boot-check-no-failures.service8 ++ for a service that implements a generic system health check and orders itself before ++ boot-complete.target. ++ ++ See ++ systemd-bless-boot.service8 ++ for a service that propagates boot success information to the boot loader, and orders itself after ++ boot-complete.target. ++ ++ + + syslog.socket + diff --git a/SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch b/SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch new file mode 100644 index 0000000..c5bd3e1 --- /dev/null +++ b/SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch @@ -0,0 +1,30 @@ +From 37f2576684d7494c916fd1f13275982f3c43f44f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 22 Apr 2020 20:34:02 +0200 +Subject: [PATCH] core: make sure to restore the control command id, too + +Fixes: #15356 +(cherry picked from commit e9da62b18af647bfa73807e1c7fc3bfa4bb4b2ac) + +Resolves: #1829867 +--- + src/core/service.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 89b41f6783..7cff419e4e 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2703,9 +2703,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char + break; + } + +- if (command && control) ++ if (command && control) { + s->control_command = command; +- else if (command) ++ s->control_command_id = id; ++ } else if (command) + s->main_command = command; + else + log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed."); diff --git a/SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch b/SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch new file mode 100644 index 0000000..04c1355 --- /dev/null +++ b/SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch @@ -0,0 +1,48 @@ +From 45d093a37b6f8c2ceae9bfd090c5265f35413b46 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Tue, 8 Sep 2020 14:51:39 +0200 +Subject: [PATCH] cgroup: freezer action must be NOP when cgroup v2 freezer is + not available + +Low-level cgroup freezer state manipulation is invoked directly from the +job engine when we are about to execute the job in order to make sure +the unit is not frozen and job execution is not blocked because of +that. + +Currently with cgroup v1 we would needlessly do a bunch of work in the +function and even falsely update the freezer state. Don't do any of this +and skip the function silently when v2 freezer is not available. + +Following bug is fixed by this commit, + +$ systemd-run --unit foo.service /bin/sleep infinity +$ systemctl restart foo.service +$ systemctl show -p FreezerState foo.service + +Before (cgroup v1, i.e. full "legacy" mode): +FreezerState=thawing + +After: +FreezerState=running + +(cherry picked from commit 9a1e90aee556b7a30d87553a891a4175ae77ed68) + +Resolves: #1868831 +--- + src/core/cgroup.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index e0eb184fd2..f1ce070f9a 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -2936,6 +2936,9 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { + assert(u); + assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); + ++ if (!cg_freezer_supported()) ++ return 0; ++ + if (!u->cgroup_realized) + return -EBUSY; + diff --git a/SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch b/SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch new file mode 100644 index 0000000..961f814 --- /dev/null +++ b/SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch @@ -0,0 +1,32 @@ +From 65e96327360ab41d44d5383dcecc82a19fad198c Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 22 Feb 2019 15:50:55 +0100 +Subject: [PATCH] logind: don't print warning when user@.service template is + masked + +User instance of systemd is optional feature and if user@.service +template is masked then administrator most likely doesn't want --user +instances of systemd for logged in users. We don't need to be verbose +about it. + +(cherry picked from commit 03b6fa0c5b51b0d39334ff6ba183a3391443bcf6) + +Resolves: #1880270 +--- + src/login/logind-user.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 8c4cd54a29..56b8066f12 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -326,7 +326,8 @@ static int user_start_service(User *u) { + &job); + if (r < 0) + /* we don't fail due to this, let's try to continue */ +- log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); ++ 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)); + else + u->service_job = job; + diff --git a/SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch b/SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch new file mode 100644 index 0000000..6ea481d --- /dev/null +++ b/SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch @@ -0,0 +1,80 @@ +From a6d76bf2d21e01a2e031e204966d946925ecc3f6 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Mon, 17 Aug 2020 14:29:04 +0200 +Subject: [PATCH] build: use simple project version in pkgconfig files + +Loosely based on commit a67c318df8800ba98d7361308937ed276dc73982. + +Resolves: #1862714 +--- + meson.build | 2 ++ + src/core/systemd.pc.in | 2 +- + src/libsystemd/libsystemd.pc.in | 2 +- + src/libudev/libudev.pc.in | 2 +- + src/udev/udev.pc.in | 2 +- + 5 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/meson.build b/meson.build +index 0ba3f924ea..65c1d0785e 100644 +--- a/meson.build ++++ b/meson.build +@@ -27,12 +27,14 @@ endif + # names, sometimes. Not all variables are included in every + # set. Ugh, ugh, ugh! + conf = configuration_data() ++conf.set_quoted('PROJECT_VERSION', meson.project_version()) + conf.set_quoted('PACKAGE_STRING', meson.project_name() + ' ' + dist_version) + conf.set_quoted('PACKAGE_VERSION', dist_version) + + substs = configuration_data() + substs.set('PACKAGE_URL', 'https://www.freedesktop.org/wiki/Software/systemd') + substs.set('PACKAGE_VERSION', dist_version) ++substs.set('PROJECT_VERSION', meson.project_version()) + + ##################################################################### + +diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in +index 655773ea8a..a350737cf2 100644 +--- a/src/core/systemd.pc.in ++++ b/src/core/systemd.pc.in +@@ -37,4 +37,4 @@ containeruidbasemax=@containeruidbasemax@ + Name: systemd + Description: systemd System and Service Manager + URL: @PACKAGE_URL@ +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ +diff --git a/src/libsystemd/libsystemd.pc.in b/src/libsystemd/libsystemd.pc.in +index c861905b67..85d6ebf293 100644 +--- a/src/libsystemd/libsystemd.pc.in ++++ b/src/libsystemd/libsystemd.pc.in +@@ -15,6 +15,6 @@ includedir=@includedir@ + Name: systemd + Description: systemd Library + URL: @PACKAGE_URL@ +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ + Libs: -L${libdir} -lsystemd + Cflags: -I${includedir} +diff --git a/src/libudev/libudev.pc.in b/src/libudev/libudev.pc.in +index 69f5c6463e..40b340362e 100644 +--- a/src/libudev/libudev.pc.in ++++ b/src/libudev/libudev.pc.in +@@ -14,6 +14,6 @@ includedir=@includedir@ + + Name: libudev + Description: Library to access udev device information +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ + Libs: -L${libdir} -ludev + Cflags: -I${includedir} +diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in +index e384a6f7c9..5acbb2d01a 100644 +--- a/src/udev/udev.pc.in ++++ b/src/udev/udev.pc.in +@@ -1,5 +1,5 @@ + Name: udev + Description: udev +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ + + udevdir=@udevlibexecdir@ diff --git a/SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch b/SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch new file mode 100644 index 0000000..4237fa1 --- /dev/null +++ b/SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch @@ -0,0 +1,57 @@ +From 2f584bd93d64a75ab11b5a5aa31d0b7145da5a86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 26 Apr 2019 13:37:31 +0200 +Subject: [PATCH] basic/virt: try the /proc/1/sched hack also for PID1 + +If a container manager does not set $container, we could end up +in a strange situation when detect-virt returns container-other when +run as non-pid-1 and none when run as pid-1. + +(cherry picked from commit 342bed02084c4396dd2f1054bd559bfb2699cfcb) +Resolves: #1868877 +--- + src/basic/virt.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index e05b3e6d99..dfa1525219 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -427,7 +427,6 @@ finish: + } + + int detect_container(void) { +- + static const struct { + const char *value; + int id; +@@ -456,9 +455,15 @@ int detect_container(void) { + } + + if (getpid_cached() == 1) { +- /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */ +- ++ /* If we are PID 1 we can just check our own environment variable, and that's authoritative. ++ * We distinguish three cases: ++ * - the variable is not defined → we jump to other checks ++ * - the variable is defined to an empty value → we are not in a container ++ * - anything else → some container, either one of the known ones or "container-other" ++ */ + e = getenv("container"); ++ if (!e) ++ goto check_sched; + if (isempty(e)) { + r = VIRTUALIZATION_NONE; + goto finish; +@@ -486,8 +491,9 @@ int detect_container(void) { + if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */ + log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m"); + +- /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown +- * there is not 1, we know we are in a PID namespace. and hence a container. */ ++ /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. If the PID ++ * shown there is not 1, we know we are in a PID namespace and hence a container. */ ++ check_sched: + r = read_one_line_file("/proc/1/sched", &m); + if (r >= 0) { + const char *t; diff --git a/SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch b/SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch new file mode 100644 index 0000000..f824c4d --- /dev/null +++ b/SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch @@ -0,0 +1,287 @@ +From 8cc497e735104080f6830a8f468b2724ae372990 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 3 Apr 2019 13:11:00 +0200 +Subject: [PATCH] seccomp: rework how the S[UG]ID filter is installed + +If we know that a syscall is undefined on the given architecture, don't +even try to add it. + +Try to install the filter even if some syscalls fail. Also use a helper +function to make the whole a bit less magic. + +This allows the S[UG]ID test to pass on arm64. + +(cherry picked from commit da4dc9a6748797e804b6bc92ad513d509abf581c) + +Resolves: #1860374 +--- + src/shared/seccomp-util.c | 244 +++++++++++++++++++++----------------- + 1 file changed, 138 insertions(+), 106 deletions(-) + +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index fd46b9f88d..d91fb4e269 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -1750,9 +1750,139 @@ int seccomp_lock_personality(unsigned long personality) { + 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; ++ int r, k; + + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; +@@ -1761,114 +1891,16 @@ int seccomp_restrict_suid_sgid(void) { + if (r < 0) + return r; + +- /* Checks the mode_t parameter of the following system calls: +- * +- * → chmod() + fchmod() + fchmodat() +- * → open() + creat() + openat() +- * → mkdir() + mkdirat() +- * → mknod() + mknodat() +- */ +- +- for (unsigned bit = 0; bit < 2; bit ++) { +- /* Block S_ISUID in the first iteration, S_ISGID in the second */ +- mode_t m = bit == 0 ? S_ISUID : S_ISGID; +- +- 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) +- break; +- +- 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) +- break; +- +- 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) +- break; +- +- 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) +- break; +- +- 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) +- break; +- +- 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) +- break; +- +- 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) +- break; +- +- 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) +- break; ++ 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)); + +- 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) +- break; ++ 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)); + +- 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) +- break; +- } +- if (r < 0) { +- log_debug_errno(r, "Failed to add suid/sgid rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ if (r < 0 && k < 0) + continue; +- } + + r = seccomp_load(seccomp); + if (IN_SET(r, -EPERM, -EACCES)) diff --git a/SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch b/SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch new file mode 100644 index 0000000..09874a7 --- /dev/null +++ b/SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch @@ -0,0 +1,91 @@ +From 860749038f508617c8fc31b8292b4019b1e621ba Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Thu, 16 Jul 2020 21:22:37 +0200 +Subject: [PATCH] vconsole-setup: downgrade log message when setting font fails + on dummy console + +Since commit 883eb9be985fd86d9cabe967eeeab91cdd396a81, vconsole-setup might be +called again to operate on dummy console where font operations are not +supported but where it's still important to have the correct keymap set [0][1]. + +vconsole-setup is mainly called by udev but can also be run via a dependency of +an early service. Both cases might end up calling vconsole-setup on the dummy +console. + +The first case can happen during early boot even on systems that use (instead +of the dummy console) a "simple" video console driver supporting font +operations (such as vgacon) until a more specific driver (such as i915) takes +the console over. While this is happening vgacon is deactivated and temporarly +replaced by the dummy console [2]. + +There are also other cases where systemd-vconsole-setup might be called on +dummy console especially during (very) early boot. Indeed +systemd-vconsole-setup.service might be pulled in by early interactive services +such as 'dracut-cmdline-ask.service` which is run before udev. + +If that happens on platforms with no grapical HWs (such as embedded ARM) or +with dummy console initially installed until a driver takes over (like Xen and +xen-fbfront) then setting font will fail. + +Therefore this patch downgrades the log message emitted when setting font fails +to LOG_DEBUG and when font operations is not implemented like it's the case for +the dummy console. + +Fixes: #16406. + +[0] https://github.com/systemd/systemd/issues/10826 +[1] https://bugzilla.redhat.com/show_bug.cgi?id=1652473 +[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/vga/vgaarb.c?h=v5.7#n204 + +(cherry picked from commit 0ef1adf51274960358e852d3bc36ae6c288a70d9) + +Resolves: #1889996 +--- + src/vconsole/vconsole-setup.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c +index f162d29220..1b406c0bc5 100644 +--- a/src/vconsole/vconsole-setup.c ++++ b/src/vconsole/vconsole-setup.c +@@ -222,6 +222,7 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { + _cleanup_free_ struct unipair* unipairs = NULL; + _cleanup_free_ void *fontbuf = NULL; + unsigned i; ++ int log_level; + int r; + + unipairs = new(struct unipair, USHRT_MAX); +@@ -230,11 +231,20 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { + return; + } + ++ log_level = LOG_WARNING; ++ + /* get metadata of the current font (width, height, count) */ + r = ioctl(src_fd, KDFONTOP, &cfo); +- if (r < 0) +- log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to get the font metadata: %m"); +- else { ++ if (r < 0) { ++ /* We might be called to operate on the dummy console (to setup keymap ++ * mainly) when fbcon deferred takeover is used for example. In such case, ++ * setting font is not supported and is expected to fail. */ ++ if (errno == ENOSYS) ++ log_level = LOG_DEBUG; ++ ++ log_full_errno(log_level, errno, ++ "KD_FONT_OP_GET failed while trying to get the font metadata: %m"); ++ } else { + /* verify parameter sanity first */ + if (cfo.width > 32 || cfo.height > 32 || cfo.charcount > 512) + log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)", +@@ -269,7 +279,7 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { + } + + if (cfo.op != KD_FONT_OP_SET) +- log_warning("Fonts will not be copied to remaining consoles"); ++ log_full(log_level, "Fonts will not be copied to remaining consoles"); + + for (i = 1; i <= 63; i++) { + char ttyname[sizeof("/dev/tty63")]; diff --git a/SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch b/SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch new file mode 100644 index 0000000..4d6bd2e --- /dev/null +++ b/SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch @@ -0,0 +1,26 @@ +From 46fa8ff1a62e3334582a971cc6bbd9b8a16680d5 Mon Sep 17 00:00:00 2001 +From: Michael Biebl +Date: Thu, 7 Mar 2019 12:02:53 +0100 +Subject: [PATCH] units: fix systemd.special man page reference in + system-update-cleanup.service + +(cherry picked from commit faab72d16b310c17be4b908cfe15eca122d16ae4) + +Resolves: #1871827 +--- + units/system-update-cleanup.service | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/system-update-cleanup.service b/units/system-update-cleanup.service +index 58baab3023..d5eca2546b 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/SOURCES/0442-units-drop-reference-to-sushell-man-page.patch b/SOURCES/0442-units-drop-reference-to-sushell-man-page.patch new file mode 100644 index 0000000..d998724 --- /dev/null +++ b/SOURCES/0442-units-drop-reference-to-sushell-man-page.patch @@ -0,0 +1,27 @@ +From 5aa59d172189adcbd7f9dedb3b909c6bf9b609f2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 29 Apr 2019 16:10:51 +0200 +Subject: [PATCH] units: drop reference to sushell man page + +sushell was a Fedoraism, and has been removed since. Hence our upstream +unit files shouldn't reference it either. + +(cherry picked from commit 6dc14d73664390682d47d7e5bcbdbb362d04f623) + +Resolves: #1871827 +--- + units/debug-shell.service.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/units/debug-shell.service.in b/units/debug-shell.service.in +index 1127e68b63..9f3868e106 100644 +--- a/units/debug-shell.service.in ++++ b/units/debug-shell.service.in +@@ -9,7 +9,6 @@ + + [Unit] + Description=Early root shell on @DEBUGTTY@ FOR DEBUGGING ONLY +-Documentation=man:sushell(8) + Documentation=man:systemd-debug-generator(8) + DefaultDependencies=no + IgnoreOnIsolate=yes diff --git a/SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch b/SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch new file mode 100644 index 0000000..a53c450 --- /dev/null +++ b/SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch @@ -0,0 +1,44 @@ +From 6a50c735a3bbf98d06fbfa7815f7bdc14ea96f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 14 Oct 2020 14:03:13 +0200 +Subject: [PATCH] sd-bus: break the loop in bus_ensure_running() if the bus is + not connecting + +This might fix #17025: +> the call trace is +> bus_ensure_running -> sd_bus_process -> bus_process_internal -> process_closeing --> sd_bus_close +> | +> \-> process_match + +We ended doing callouts to the Disconnected matches from bus_ensure_running() +and shouldn't. bus_ensure_running() should never do callouts. This change +should fix this however: once we notice that the connection is going down we +will now fail instantly with ENOTOCONN instead of calling any callbacks. + +(cherry picked from commit 93a59b1ae5d3bcb0ec1488ebc13d0d1ff4d1729a) + +Resolves: #1885553 +--- + src/libsystemd/sd-bus/sd-bus.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index a3509f7e89..c65e24b2d1 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -2059,12 +2059,13 @@ int bus_ensure_running(sd_bus *bus) { + + assert(bus); + +- if (IN_SET(bus->state, BUS_UNSET, BUS_CLOSED, BUS_CLOSING)) +- return -ENOTCONN; + if (bus->state == BUS_RUNNING) + return 1; + + for (;;) { ++ if (IN_SET(bus->state, BUS_UNSET, BUS_CLOSED, BUS_CLOSING)) ++ return -ENOTCONN; ++ + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; diff --git a/SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch b/SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch new file mode 100644 index 0000000..ccd3ae1 --- /dev/null +++ b/SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch @@ -0,0 +1,831 @@ +From 7155c010ef8c620295d230c284849636c07b40c0 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 22 Mar 2019 20:57:30 +0100 +Subject: [PATCH] core: add new API for enqueing a job with returning the + transaction data + +(cherry picked from commit 50cbaba4fe5a32850998682699322d012e597e4a) + +Related: #846319 +--- + src/analyze/analyze-verify.c | 2 +- + src/core/automount.c | 4 +- + src/core/dbus-manager.c | 23 +++++- + src/core/dbus-unit.c | 153 +++++++++++++++++++++++++++++++---- + src/core/dbus-unit.h | 8 +- + src/core/dbus.c | 2 +- + src/core/device.c | 2 +- + src/core/emergency-action.c | 5 +- + src/core/main.c | 4 +- + src/core/manager.c | 38 +++++---- + src/core/manager.h | 6 +- + src/core/path.c | 2 +- + src/core/service.c | 2 +- + src/core/socket.c | 4 +- + src/core/timer.c | 2 +- + src/core/transaction.c | 22 ++++- + src/core/transaction.h | 2 +- + src/core/unit.c | 16 ++-- + src/test/test-engine.c | 20 ++--- + 19 files changed, 244 insertions(+), 73 deletions(-) + +diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c +index ed369532d4..1e143511b2 100644 +--- a/src/analyze/analyze-verify.c ++++ b/src/analyze/analyze-verify.c +@@ -205,7 +205,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/core/automount.c b/src/core/automount.c +index b1a155d8d4..76e70f4dac 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -776,7 +776,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; +@@ -1032,7 +1032,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/dbus-manager.c b/src/core/dbus-manager.c +index a0777f63d5..0a1d3df42f 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -549,6 +549,26 @@ 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); + } + ++static int method_enqueue_unit_job(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 = manager_load_unit(m, name, NULL, error, &u); ++ if (r < 0) ++ return r; ++ ++ return bus_unit_method_enqueue_job(message, u, error); ++} ++ + static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *old_name; +@@ -978,7 +998,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) { +@@ -2547,6 +2567,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("FreezeUnit", "s", NULL, method_freeze_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ThawUnit", "s", NULL, method_thaw_unit, SD_BUS_VTABLE_UNPRIVILEGED), +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index ce81103e92..549a166abc 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -314,6 +314,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, +@@ -324,13 +332,6 @@ int bus_unit_method_start_generic( + const char *smode; + 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); +@@ -372,7 +373,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) { +@@ -403,6 +405,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; +@@ -722,6 +780,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), +@@ -1354,11 +1413,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); +@@ -1373,7 +1435,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) +@@ -1391,7 +1453,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; + +@@ -1399,11 +1467,64 @@ int bus_unit_queue_job( + if (r < 0) + return r; + +- path = job_dbus_path(j); +- if (!path) ++ job_path = job_dbus_path(j); ++ if (!job_path) + return -ENOMEM; + +- return sd_bus_reply_method_return(message, "o", path); ++ /* 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( +diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h +index 39aa1bb53c..d298fcc99e 100644 +--- a/src/core/dbus-unit.h ++++ b/src/core/dbus-unit.h +@@ -15,6 +15,7 @@ int bus_unit_send_pending_freezer_message(Unit *u); + void bus_unit_send_removed_signal(Unit *u); + + int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); ++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); + +@@ -27,7 +28,12 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error + int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error); + int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error); + +-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 b69c11c519..584a8a1b01 100644 +--- a/src/core/dbus.c ++++ b/src/core/dbus.c +@@ -176,7 +176,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; + +diff --git a/src/core/device.c b/src/core/device.c +index 021c28dfbd..cb8b66dfc5 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -419,7 +419,7 @@ static int device_add_udev_wants(Unit *u, struct udev_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)); + } +diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c +index 76e1124cff..766a3b4d2b 100644 +--- a/src/core/emergency-action.c ++++ b/src/core/emergency-action.c +@@ -54,8 +54,7 @@ int emergency_action( + log_and_status(m, "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) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL); + break; + + case EMERGENCY_ACTION_REBOOT_FORCE: +@@ -83,7 +82,7 @@ int emergency_action( + + case EMERGENCY_ACTION_POWEROFF: + log_and_status(m, "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_POWEROFF_FORCE: +diff --git a/src/core/main.c b/src/core/main.c +index 25536054b3..d897155644 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1952,13 +1952,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)); +diff --git a/src/core/manager.c b/src/core/manager.c +index 4c04896aaa..012615e537 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1242,7 +1242,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)); + } +@@ -1685,9 +1685,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); +@@ -1695,10 +1703,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)); + +@@ -1710,7 +1718,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; + +@@ -1720,7 +1728,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; + +@@ -1728,8 +1736,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; +@@ -1740,7 +1748,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; + +@@ -1754,10 +1762,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; + +@@ -1766,7 +1774,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)); + +@@ -1794,7 +1802,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; + +@@ -2512,7 +2520,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)); + } +diff --git a/src/core/manager.h b/src/core/manager.h +index 40568d3c8b..c4b8e80093 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -397,9 +397,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); +diff --git a/src/core/path.c b/src/core/path.c +index dda4a3036b..ed40bc6c19 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; + +diff --git a/src/core/service.c b/src/core/service.c +index 7cff419e4e..5e3e75b5ae 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2176,7 +2176,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; + +diff --git a/src/core/socket.c b/src/core/socket.c +index 7c6d3dfad1..fe061eb73b 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2274,7 +2274,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; + } +@@ -2349,7 +2349,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. */ +diff --git a/src/core/timer.c b/src/core/timer.c +index 2876d54a59..281ac7f97f 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -566,7 +566,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; + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index 045930838b..cdaaff4f55 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -585,7 +585,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; +@@ -642,6 +647,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; +@@ -654,7 +664,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; +@@ -731,7 +747,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 70d74a4ccb..4b5620f5c8 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 29ce6c1fb7..ffbf3cfd48 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -2033,7 +2033,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)); + } +@@ -2049,25 +2049,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) { +@@ -2081,7 +2081,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) { +@@ -2100,7 +2100,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)); + } +diff --git a/src/test/test-engine.c b/src/test/test-engine.c +index 0f3e244dc1..f2e327b3f5 100644 +--- a/src/test/test-engine.c ++++ b/src/test/test-engine.c +@@ -46,7 +46,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); +@@ -59,15 +59,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"); +@@ -75,21 +75,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"); +@@ -97,7 +97,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/SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch b/SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch new file mode 100644 index 0000000..b95326c --- /dev/null +++ b/SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch @@ -0,0 +1,115 @@ +From 8b34041ee97069bee8bf03ae5ba651b34b1b8460 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 15:20:26 +0100 +Subject: [PATCH] systemctl: replace switch statement by table of structures + +(cherry picked from commit c45e5fb877033c9e3f9b79121644ed71032af379) + +Related: #846319 +--- + src/systemctl/systemctl.c | 68 ++++++++++++--------------------------- + 1 file changed, 21 insertions(+), 47 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index e963f19b0a..04e24691d8 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3179,64 +3179,38 @@ static int logind_set_wall_message(void) { + } + #endif + +-/* Ask systemd-logind, which might grant access to unprivileged users +- * through PolicyKit */ ++/* 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; + +@@ -3245,12 +3219,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 diff --git a/SOURCES/0446-systemctl-reindent-table.patch b/SOURCES/0446-systemctl-reindent-table.patch new file mode 100644 index 0000000..22ad788 --- /dev/null +++ b/SOURCES/0446-systemctl-reindent-table.patch @@ -0,0 +1,53 @@ +From 26d2d89c6216672cedf6abfe1b73081adebbdcf8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 15:49:52 +0100 +Subject: [PATCH] systemctl: reindent table + +(cherry picked from commit 5fd77930ad9980af5257f9f871556d6973db736c) + +Related: #846319 +--- + src/systemctl/systemctl.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 04e24691d8..f057dc829c 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -2974,21 +2974,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" }, + }; + diff --git a/SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch b/SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch new file mode 100644 index 0000000..2cd38d4 --- /dev/null +++ b/SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch @@ -0,0 +1,29 @@ +From 91c83bde0904581fbc33eb7821119e665b9505ce Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Fri, 20 Jul 2018 11:32:55 -0700 +Subject: [PATCH] systemctl: Only wait when there's something to wait for. + +Tested: +- `systemctl --wait start i-do-not-exist.service` does not wait. +- `systemctl --wait start i-do-not-exist.service valid-unit.service` does. + +(cherry picked from commit 46f2579c2ac9f6780d5afec1000764defc6b581e) + +Related: #846319 +--- + src/systemctl/systemctl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index f057dc829c..1929692480 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3130,7 +3130,7 @@ static int start_unit(int argc, char *argv[], void *userdata) { + check_triggering_units(bus, *name); + } + +- if (r >= 0 && arg_wait) { ++ if (r >= 0 && arg_wait && !set_isempty(wait_context.unit_paths)) { + int q; + q = sd_event_loop(wait_context.event); + if (q < 0) diff --git a/SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch b/SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch new file mode 100644 index 0000000..ffd9a76 --- /dev/null +++ b/SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch @@ -0,0 +1,99 @@ +From 7569756d005d4f780fffd2504eb6f461982833f2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 13 Oct 2018 14:38:46 +0200 +Subject: [PATCH] systemctl: clean up start_unit_one() error handling + +Let's split exit code handling in two: "r" is only used for errno-style +errors, and "ret" is used for exit() codes. Then, let's use EXIT_SUCCESS +for checking whether the latter is already used. + +This way it should always be clear what kind of error we are processing, +and when we propaate one into the other. + +Moreover this allows us to drop "q" form all inner loops, avoiding +confusion when to use "q" and when "r" to store received errors. + +Fixes: #9704 +(cherry picked from commit 0e8d9c0c4d7e71487c486f626c59853cfb031d16) + +Related: #846319 +--- + src/systemctl/systemctl.c | 32 +++++++++++++++----------------- + 1 file changed, 15 insertions(+), 17 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 1929692480..4af9deb98d 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3004,12 +3004,12 @@ static enum action verb_to_action(const char *verb) { + + static int start_unit(int argc, char *argv[], void *userdata) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; ++ _cleanup_(wait_context_free) WaitContext wait_context = {}; + const char *method, *mode, *one_name, *suffix = NULL; + _cleanup_strv_free_ char **names = NULL; ++ int r, ret = EXIT_SUCCESS; + sd_bus *bus; +- _cleanup_(wait_context_free) WaitContext wait_context = {}; + char **name; +- int r = 0; + + if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) { + log_error("--wait may only be used with the 'start' or 'restart' commands."); +@@ -3096,16 +3096,15 @@ static int start_unit(int argc, char *argv[], void *userdata) { + + STRV_FOREACH(name, names) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- int q; + +- q = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); +- if (r >= 0 && q < 0) +- r = translate_bus_error_to_exit_status(q, &error); ++ r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); ++ if (ret == EXIT_SUCCESS && r < 0) ++ ret = translate_bus_error_to_exit_status(r, &error); + } + + if (!arg_no_block) { +- int q, arg_count = 0; + const char* extra_args[4] = {}; ++ int arg_count = 0; + + if (arg_scope != UNIT_FILE_SYSTEM) + extra_args[arg_count++] = "--user"; +@@ -3119,9 +3118,9 @@ static int start_unit(int argc, char *argv[], void *userdata) { + extra_args[arg_count++] = arg_host; + } + +- q = bus_wait_for_jobs(w, arg_quiet, extra_args); +- if (q < 0) +- return q; ++ r = bus_wait_for_jobs(w, arg_quiet, extra_args); ++ if (r < 0) ++ return r; + + /* When stopping units, warn if they can still be triggered by + * another active unit (socket, path, timer) */ +@@ -3130,16 +3129,15 @@ static int start_unit(int argc, char *argv[], void *userdata) { + check_triggering_units(bus, *name); + } + +- if (r >= 0 && arg_wait && !set_isempty(wait_context.unit_paths)) { +- int q; +- q = sd_event_loop(wait_context.event); +- if (q < 0) +- return log_error_errno(q, "Failed to run event loop: %m"); ++ if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) { ++ r = sd_event_loop(wait_context.event); ++ if (r < 0) ++ return log_error_errno(r, "Failed to run event loop: %m"); + if (wait_context.any_failed) +- r = EXIT_FAILURE; ++ ret = EXIT_FAILURE; + } + +- return r; ++ return ret; + } + + #if ENABLE_LOGIND diff --git a/SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch b/SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch new file mode 100644 index 0000000..a580c7a --- /dev/null +++ b/SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch @@ -0,0 +1,68 @@ +From cfb124260a0a9e68102a373c0d136f792e2d4ea7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 16:19:35 +0100 +Subject: [PATCH] systemctl: split out extra args generation into helper + function of its own + +(cherry picked from commit 94369fc0663255bbd327f97dba288ececf51a514) + +Related: #846319 +--- + src/systemctl/systemctl.c | 36 +++++++++++++++++++++--------------- + 1 file changed, 21 insertions(+), 15 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 4af9deb98d..e7a8fd559f 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3002,6 +3002,25 @@ 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 = {}; +@@ -3103,22 +3122,9 @@ static int start_unit(int argc, char *argv[], void *userdata) { + } + + if (!arg_no_block) { +- const char* extra_args[4] = {}; +- int arg_count = 0; +- +- 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; +- } ++ const char* extra_args[4]; + +- 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; + diff --git a/SOURCES/0450-systemctl-add-new-show-transaction-switch.patch b/SOURCES/0450-systemctl-add-new-show-transaction-switch.patch new file mode 100644 index 0000000..1e804b0 --- /dev/null +++ b/SOURCES/0450-systemctl-add-new-show-transaction-switch.patch @@ -0,0 +1,362 @@ +From cacf7a619cdccd9a6da6c2fe7361eac121b9ea0b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 22 Mar 2019 20:58:13 +0100 +Subject: [PATCH] systemctl: add new --show-transaction switch + +This new switch uses the new method call EnqueueUnitJob() for enqueuing +a job and showing the jobs it enqueued. + +Fixes: #2297 +(cherry picked from commit 85d9b5981ba6b7ee3955f95fa6cf3bb8cdf3444d) + +Resolves: #846319 +--- + src/systemctl/systemctl.c | 183 ++++++++++++++++++++++++++------------ + 1 file changed, 128 insertions(+), 55 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index e7a8fd559f..8bec798373 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -126,6 +126,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; +@@ -734,7 +735,6 @@ static int get_unit_list_recursive( + *_machines = NULL; + + *_unit_infos = TAKE_PTR(unit_infos); +- + *_replies = TAKE_PTR(replies); + + return c; +@@ -2688,25 +2688,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)) +@@ -2715,14 +2716,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 { +@@ -2805,7 +2806,8 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error + + 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, +@@ -2814,6 +2816,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); +@@ -2859,44 +2862,81 @@ static int start_unit_one( + 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_copy(error, &enqueue_error); ++ sd_bus_error_free(&enqueue_error); ++ goto fail; ++ } ++ ++ /* 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; + +- verb = method_to_verb(method); ++ r = sd_bus_message_read(reply, "uosos", &id, &path, &u, NULL, &jt); ++ if (r < 0) ++ return bus_log_parse_error(r); + +- log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r)); ++ log_info("Enqueued anchor job %" PRIu32 " %s/%s.", id, u, jt); + +- 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); ++ 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; + +- return r; ++ 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); +@@ -2909,6 +2949,24 @@ static int start_unit_one( + } + + 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) { +@@ -2965,7 +3023,6 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r + } + + *ret = TAKE_PTR(mangled); +- + return 0; + } + +@@ -3024,7 +3081,7 @@ static const char** make_extra_args(const char *extra_args[static 4]) { + 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_strv_free_ char **names = NULL; + int r, ret = EXIT_SUCCESS; + sd_bus *bus; +@@ -3050,27 +3107,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; + } +@@ -3105,9 +3169,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"); +@@ -3116,7 +3182,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); + } +@@ -7167,6 +7233,8 @@ static void systemctl_help(void) { + " --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" +@@ -7482,6 +7550,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, + { "now", no_argument, NULL, ARG_NOW }, + { "message", required_argument, NULL, ARG_MESSAGE }, ++ { "show-transaction", no_argument, NULL, 'T' }, + {} + }; + +@@ -7494,7 +7563,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) { + +@@ -7815,6 +7884,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + return log_oom(); + break; + ++ case 'T': ++ arg_show_transaction = true; ++ break; ++ + case '?': + return -EINVAL; + diff --git a/SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch b/SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch new file mode 100644 index 0000000..13c3808 --- /dev/null +++ b/SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch @@ -0,0 +1,31 @@ +From f31afbfd2fa68e20a10a8432fb4714a6d4e1170a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 17:39:36 +0100 +Subject: [PATCH] test: add some basic testing that "systemctl start -T" does + something + +(cherry picked from commit f087c7e072bb338d5c7c0781c9fbc900612efd18) + +Related: #846319 +--- + test/TEST-03-JOBS/test-jobs.sh | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh +index e66ea53621..42190cf478 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/SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch b/SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch new file mode 100644 index 0000000..1f8b8ce --- /dev/null +++ b/SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch @@ -0,0 +1,37 @@ +From 588e3e8008d24021ec025d54318fb07d2212293c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 18:02:49 +0100 +Subject: [PATCH] man: document the new systemctl --show-transaction option + +(cherry picked from commit df4a7cb7323c8cf00553d766913312c5b7ccd508) + +Related: #846319 +--- + man/systemctl.xml | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index 6145486123..fa08ab6c0a 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -298,6 +298,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. ++ ++ ++ + + + diff --git a/SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch b/SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch new file mode 100644 index 0000000..357f076 --- /dev/null +++ b/SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch @@ -0,0 +1,153 @@ +From 262544a451c11c38e92c45047ec2adeaeb2a0a7e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Renaud=20M=C3=A9trich?= +Date: Thu, 20 Aug 2020 13:00:37 +0200 +Subject: [PATCH] socket: New option 'FlushPending' (boolean) to flush socket + before entering listening state + +Disabled by default. When Enabled, before listening on the socket, flush the content. +Applies when Accept=no only. + +(cherry picked from commit 3e5f04bf6468fcb79c080f02b0eab08f258bff0c) + +Resolves: #1870638 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.socket.xml | 12 ++++++++++++ + src/core/dbus-socket.c | 4 ++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/socket.c | 11 +++++++++++ + src/core/socket.h | 1 + + src/shared/bus-unit-util.c | 3 ++- + 7 files changed, 32 insertions(+), 1 deletion(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 1a4e79190a..995b8797ef 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -388,6 +388,7 @@ Most socket unit settings are available to transient units. + ✓ SocketMode= + ✓ DirectoryMode= + ✓ Accept= ++✓ FlushPending= + ✓ Writable= + ✓ MaxConnections= + ✓ MaxConnectionsPerSource= +diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml +index 19c2ca9907..8676b4e03f 100644 +--- a/man/systemd.socket.xml ++++ b/man/systemd.socket.xml +@@ -425,6 +425,18 @@ + false, in read-only mode. Defaults to false. + + ++ ++ FlushPending= ++ Takes a boolean argument. May only be used when ++ . If yes, the socket's buffers are cleared after the ++ triggered service exited. This causes any pending data to be ++ flushed and any pending incoming connections to be rejected. If no, the ++ socket's buffers won't be cleared, permitting the service to handle any ++ pending connections after restart, which is the usually expected behaviour. ++ Defaults to . ++ ++ ++ + + MaxConnections= + The maximum number of connections to +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index 913cc74918..bb77539030 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -85,6 +85,7 @@ const sd_bus_vtable bus_socket_vtable[] = { + SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("FlushPending", "b", bus_property_get_bool, offsetof(Socket, flush_pending), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -177,6 +178,9 @@ static int bus_socket_set_transient_property( + if (streq(name, "Accept")) + return bus_set_transient_bool(u, name, &s->accept, message, flags, error); + ++ if (streq(name, "FlushPending")) ++ return bus_set_transient_bool(u, name, &s->flush_pending, message, flags, error); ++ + if (streq(name, "Writable")) + return bus_set_transient_bool(u, name, &s->writable, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 6d21b2e433..24ee5ae6fe 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -359,6 +359,7 @@ Socket.SocketGroup, config_parse_user_group, 0, + Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) + Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) + Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) ++Socket.FlushPending, config_parse_bool, 0, offsetof(Socket, flush_pending) + Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable) + Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) + Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source) +diff --git a/src/core/socket.c b/src/core/socket.c +index fe061eb73b..97c3a7fc9a 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -70,6 +70,7 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { + + static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); ++static void flush_ports(Socket *s); + + static void socket_init(Unit *u) { + Socket *s = SOCKET(u); +@@ -703,6 +704,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { + prefix, s->n_connections, + prefix, s->max_connections, + prefix, s->max_connections_per_source); ++ else ++ fprintf(f, ++ "%sFlushPending: %s\n", ++ prefix, yes_no(s->flush_pending)); ++ + + if (s->priority >= 0) + fprintf(f, +@@ -2111,6 +2117,11 @@ static void socket_enter_listening(Socket *s) { + int r; + assert(s); + ++ if (!s->accept && s->flush_pending) { ++ log_unit_debug(UNIT(s), "Flushing socket before listening."); ++ flush_ports(s); ++ } ++ + r = socket_watch_fds(s); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); +diff --git a/src/core/socket.h b/src/core/socket.h +index c4e25db1fc..b7a25d91fd 100644 +--- a/src/core/socket.h ++++ b/src/core/socket.h +@@ -109,6 +109,7 @@ struct Socket { + bool accept; + bool remove_on_stop; + bool writable; ++ bool flush_pending; + + int socket_protocol; + +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 77788f0fe2..7029aa5615 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -1468,7 +1468,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons + + if (STR_IN_SET(field, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", +- "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) ++ "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet", ++ "FlushPending")) + + return bus_append_parse_boolean(m, field, eq); + diff --git a/SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch b/SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch new file mode 100644 index 0000000..2d454d8 --- /dev/null +++ b/SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch @@ -0,0 +1,62 @@ +From 9c0ed82f661a2296784970678746b0b4f130870e Mon Sep 17 00:00:00 2001 +From: Alan Jenkins +Date: Thu, 21 Jun 2018 14:12:30 +0100 +Subject: [PATCH] core: remove support for API bus "started outside our own + logic" + +Looking at a recent Bad Day, my log contains over 100 lines of + + systemd[23895]: Failed to connect to API bus: Connection refused + +It is due to "systemd --user" retrying to connect to an API bus.[*] I +would prefer to avoid spamming the logs. I don't think it is good for us +to retry so much like this. + +systemd was mislead by something setting DBUS_SESSION_BUS_ADDRESS. My best +guess is an unfortunate series of events caused gdm to set this. gdm has +code to start a session dbus if there is not a bus available already (and +in this case it exports the environment variable). I believe it does not +normally do this when running under systemd, because "systemd --user" and +hence "dbus.service" would already have been started by pam_systemd. + +I see two possibilities + +1. Rip out the check for DBUS_SESSION_BUS_ADDRESS entirely. +2. Only check for DBUS_SESSION_BUS_ADDRESS on startup. Not in the + "recheck" logic. + +The justification for 2), is that the recheck is called from unit_notify(), +this is used to check whether the service just started (or stopped) was +"dbus.service". This reason for rechecking does not apply if we think +the session bus was started outside our logic. + +But I think we can justify 1). dbus-daemon ships a statically-enabled +/usr/lib/systemd/user/dbus.service, which would conflict with an attempt to +use an external dbus. Also "systemd --user" is started from user@.service; +if you try to start it manually so that it inherits an environment +variable, it will conflict if user@.service was started by pam_systemd +(or loginctl enable-linger). + +(cherry picked from commit d3243f55ca9b5f305306ba4105ab29768e372a78) + +Resolves: #1764282 +--- + src/core/manager.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index 012615e537..3c44ad3dbc 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1519,11 +1519,6 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) { + if (m->test_run_flags != 0) + return false; + +- /* If we are in the user instance, and the env var is already set for us, then this means D-Bus is ran +- * somewhere outside of our own logic. Let's use it */ +- if (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")) +- return true; +- + u = manager_get_unit(m, SPECIAL_DBUS_SOCKET); + if (!u) + return false; diff --git a/SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch b/SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch new file mode 100644 index 0000000..a99e7de --- /dev/null +++ b/SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch @@ -0,0 +1,56 @@ +From 1c8d1c3bbdf8bdfb2ee4f85b9f559f54c6e4cac4 Mon Sep 17 00:00:00 2001 +From: Wen Yang +Date: Wed, 1 Jul 2020 04:45:33 +0800 +Subject: [PATCH] mount-setup: fix segfault in mount_cgroup_controllers when + using gcc9 compiler + +According to the documentation: +https://gcc.gnu.org/gcc-9/porting_to.html#complit + +The 'join_controllers' that relied on the extended lifetime needs +to be fixed, move the compound literals to the function scope it +need to accessible in. + +Resolves: #1868877 +--- + src/core/mount-setup.c | 24 +++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c +index 16880e6157..a6594580e5 100644 +--- a/src/core/mount-setup.c ++++ b/src/core/mount-setup.c +@@ -237,20 +237,22 @@ int mount_cgroup_controllers(char ***join_controllers) { + if (!cg_is_legacy_wanted()) + return 0; + ++ /* The defaults: ++ * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio". ++ * ++ * We'd like to add "cpuset" to the mix, but "cpuset" doesn't really ++ * work for groups with no initialized attributes. ++ */ ++ char ***default_join_controllers = (char**[]) { ++ STRV_MAKE("cpu", "cpuacct"), ++ STRV_MAKE("net_cls", "net_prio"), ++ NULL, ++ }; ++ + /* Mount all available cgroup controllers that are built into the kernel. */ + + if (!has_argument) +- /* The defaults: +- * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio". +- * +- * We'd like to add "cpuset" to the mix, but "cpuset" doesn't really +- * work for groups with no initialized attributes. +- */ +- join_controllers = (char**[]) { +- STRV_MAKE("cpu", "cpuacct"), +- STRV_MAKE("net_cls", "net_prio"), +- NULL, +- }; ++ join_controllers = default_join_controllers; + + r = cg_kernel_controllers(&controllers); + if (r < 0) diff --git a/SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch b/SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch new file mode 100644 index 0000000..71758ee --- /dev/null +++ b/SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch @@ -0,0 +1,39 @@ +From 1730f7bb306e13689a7684fd93ae5b8383a28d2f Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 31 May 2019 15:23:23 +0200 +Subject: [PATCH] dbus-execute: make transfer of CPUAffinity endian safe + (#12711) + +We store the affinity mask in the native endian. However, over D-Bus we +must transfer the mask in little endian byte order. + +This is the second part of c367f996f5f091a63f812f0140b304c649be77fc. + +(cherry picked from commit 75e40119a471454516ad0acc96f6f4094e7fb652) + +Related: #1740657 +--- + src/core/dbus-execute.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index f9527e56b2..d5acca384f 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -215,12 +215,15 @@ static int property_get_cpu_affinity( + sd_bus_error *error) { + + ExecContext *c = userdata; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; + + assert(bus); + assert(reply); + assert(c); + +- return sd_bus_message_append_array(reply, 'y', c->cpu_set.set, c->cpu_set.allocated); ++ (void) cpu_set_to_dbus(&c->cpu_set, &array, &allocated); ++ return sd_bus_message_append_array(reply, 'y', array, allocated); + } + + static int property_get_numa_mask( diff --git a/SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch b/SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch new file mode 100644 index 0000000..3118171 --- /dev/null +++ b/SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch @@ -0,0 +1,426 @@ +From 1a822dbe19ab6634ffb2c0d3ce92b27b503e1612 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 17 Feb 2020 13:50:31 +0100 +Subject: [PATCH] core: add support for setting CPUAffinity= to special "numa" + value + +systemd will automatically derive CPU affinity mask from NUMA node +mask. + +Fixes #13248 + +(cherry picked from commit e2b2fb7f566d13a3de61952b5356cd4d2eaee917) + +Resolves: #1740657 +--- + man/systemd.exec.xml | 9 +++--- + src/basic/cpu-set-util.c | 43 ++++++++++++++++++++++++-- + src/basic/cpu-set-util.h | 1 + + src/core/dbus-execute.c | 30 +++++++++++++++++- + src/core/execute.c | 46 ++++++++++++++++++++++++++-- + src/core/execute.h | 3 ++ + src/core/load-fragment.c | 14 ++++++++- + src/shared/bus-unit-util.c | 9 ++++++ + src/test/test-cpu-set-util.c | 6 ++-- + test/TEST-36-NUMAPOLICY/testsuite.sh | 18 +++++++++++ + 10 files changed, 166 insertions(+), 13 deletions(-) + +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index e2a5ede968..696438c4ef 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -706,10 +706,11 @@ CapabilityBoundingSet=~CAP_B CAP_C + CPUAffinity= + + Controls the CPU affinity of the executed processes. Takes a list of CPU indices or ranges +- separated by either whitespace or commas. CPU ranges are specified by the lower and upper CPU indices separated +- by a dash. This option may be specified more than once, in which case the specified CPU affinity masks are +- merged. If the empty string is assigned, the mask is reset, all assignments prior to this will have no +- effect. See ++ separated by either whitespace or commas. Alternatively, takes a special "numa" value in which case systemd ++ automatically derives allowed CPU range based on the value of NUMAMask= option. CPU ranges ++ are specified by the lower and upper CPU indices separated by a dash. This option may be specified more than ++ once, in which case the specified CPU affinity masks are merged. If the empty string is assigned, the mask ++ is reset, all assignments prior to this will have no effect. See + sched_setaffinity2 for + details. + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 51752ad1a6..1922c95864 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -12,12 +12,14 @@ + #include "cpu-set-util.h" + #include "dirent-util.h" + #include "extract-word.h" ++#include "fileio.h" + #include "fd-util.h" + #include "log.h" + #include "macro.h" + #include "missing.h" + #include "parse-util.h" + #include "stat-util.h" ++#include "stdio-util.h" + #include "string-util.h" + #include "string-table.h" + #include "strv.h" +@@ -179,7 +181,7 @@ int cpu_set_add_all(CPUSet *a, const CPUSet *b) { + return r; + } + +- return 0; ++ return 1; + } + + int parse_cpu_set_full( +@@ -264,7 +266,7 @@ int parse_cpu_set_extend( + if (!old->set) { + *old = cpuset; + cpuset = (CPUSet) {}; +- return 0; ++ return 1; + } + + return cpu_set_add_all(old, &cpuset); +@@ -417,6 +419,43 @@ int apply_numa_policy(const NUMAPolicy *policy) { + return 0; + } + ++int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret) { ++ int r; ++ size_t i; ++ _cleanup_(cpu_set_reset) CPUSet s = {}; ++ ++ assert(policy); ++ assert(ret); ++ ++ for (i = 0; i < policy->nodes.allocated * 8; i++) { ++ _cleanup_free_ char *l = NULL; ++ char p[STRLEN("/sys/devices/system/node/node//cpulist") + DECIMAL_STR_MAX(size_t) + 1]; ++ _cleanup_(cpu_set_reset) CPUSet part = {}; ++ ++ if (!CPU_ISSET_S(i, policy->nodes.allocated, policy->nodes.set)) ++ continue; ++ ++ xsprintf(p, "/sys/devices/system/node/node%zu/cpulist", i); ++ ++ r = read_one_line_file(p, &l); ++ if (r < 0) ++ return r; ++ ++ r = parse_cpu_set(l, &part); ++ if (r < 0) ++ return r; ++ ++ r = cpu_set_add_all(&s, &part); ++ if (r < 0) ++ return r; ++ } ++ ++ *ret = s; ++ s = (CPUSet) {}; ++ ++ return 0; ++} ++ + static const char* const mpol_table[] = { + [MPOL_DEFAULT] = "default", + [MPOL_PREFERRED] = "preferred", +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 8519a9b6c8..795be807af 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -78,6 +78,7 @@ static inline void numa_policy_reset(NUMAPolicy *p) { + } + + int apply_numa_policy(const NUMAPolicy *policy); ++int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret); + + const char* mpol_to_string(int i) _const_; + int mpol_from_string(const char *s) _pure_; +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index d5acca384f..0fe4c14e48 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -58,6 +58,8 @@ static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, + static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL); + static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI); + static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC); ++static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa); ++ + + static int property_get_environment_files( + sd_bus *bus, +@@ -215,6 +217,7 @@ static int property_get_cpu_affinity( + sd_bus_error *error) { + + ExecContext *c = userdata; ++ _cleanup_(cpu_set_reset) CPUSet s = {}; + _cleanup_free_ uint8_t *array = NULL; + size_t allocated; + +@@ -222,7 +225,16 @@ static int property_get_cpu_affinity( + assert(reply); + assert(c); + +- (void) cpu_set_to_dbus(&c->cpu_set, &array, &allocated); ++ if (c->cpu_affinity_from_numa) { ++ int r; ++ ++ r = numa_to_cpu_set(&c->numa_policy, &s); ++ if (r < 0) ++ return r; ++ } ++ ++ (void) cpu_set_to_dbus(c->cpu_affinity_from_numa ? &s : &c->cpu_set, &array, &allocated); ++ + return sd_bus_message_append_array(reply, 'y', array, allocated); + } + +@@ -743,6 +755,7 @@ const sd_bus_vtable bus_exec_vtable[] = { + SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("CPUAffinityFromNUMA", "b", property_get_cpu_affinity_from_numa, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NUMAPolicy", "i", property_get_numa_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NUMAMask", "ay", property_get_numa_mask, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), +@@ -1639,6 +1652,20 @@ int bus_exec_context_set_transient_property( + + return 1; + ++ } else if (streq(name, "CPUAffinityFromNUMA")) { ++ int q; ++ ++ r = sd_bus_message_read_basic(message, 'b', &q); ++ if (r < 0) ++ return r; ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ c->cpu_affinity_from_numa = q; ++ unit_write_settingf(u, flags, name, "%s=%s", "CPUAffinity", "numa"); ++ } ++ ++ return 1; ++ + } else if (streq(name, "NUMAPolicy")) { + int32_t type; + +@@ -1653,6 +1680,7 @@ int bus_exec_context_set_transient_property( + c->numa_policy.type = type; + + return 1; ++ + } else if (streq(name, "IOSchedulingClass")) { + int32_t q; + +diff --git a/src/core/execute.c b/src/core/execute.c +index 3c54ac1110..d528d08830 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2750,6 +2750,33 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p + + static char *exec_command_line(char **argv); + ++static int exec_context_cpu_affinity_from_numa(const ExecContext *c, CPUSet *ret) { ++ _cleanup_(cpu_set_reset) CPUSet s = {}; ++ int r; ++ ++ assert(c); ++ assert(ret); ++ ++ if (!c->numa_policy.nodes.set) { ++ log_debug("Can't derive CPU affinity mask from NUMA mask because NUMA mask is not set, ignoring"); ++ return 0; ++ } ++ ++ r = numa_to_cpu_set(&c->numa_policy, &s); ++ if (r < 0) ++ return r; ++ ++ cpu_set_reset(ret); ++ ++ return cpu_set_add_all(ret, &s); ++} ++ ++bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) { ++ assert(c); ++ ++ return c->cpu_affinity_from_numa; ++} ++ + static int exec_child( + Unit *unit, + const ExecCommand *command, +@@ -3012,11 +3039,26 @@ static int exec_child( + } + } + +- if (context->cpu_set.set) +- if (sched_setaffinity(0, context->cpu_set.allocated, context->cpu_set.set) < 0) { ++ if (context->cpu_affinity_from_numa || context->cpu_set.set) { ++ _cleanup_(cpu_set_reset) CPUSet converted_cpu_set = {}; ++ const CPUSet *cpu_set; ++ ++ if (context->cpu_affinity_from_numa) { ++ r = exec_context_cpu_affinity_from_numa(context, &converted_cpu_set); ++ if (r < 0) { ++ *exit_status = EXIT_CPUAFFINITY; ++ return log_unit_error_errno(unit, r, "Failed to derive CPU affinity mask from NUMA mask: %m"); ++ } ++ ++ cpu_set = &converted_cpu_set; ++ } else ++ cpu_set = &context->cpu_set; ++ ++ if (sched_setaffinity(0, cpu_set->allocated, cpu_set->set) < 0) { + *exit_status = EXIT_CPUAFFINITY; + return log_unit_error_errno(unit, errno, "Failed to set up CPU affinity: %m"); + } ++ } + + if (mpol_is_valid(numa_policy_get_type(&context->numa_policy))) { + r = apply_numa_policy(&context->numa_policy); +diff --git a/src/core/execute.h b/src/core/execute.h +index 86c1cee84c..62c6229621 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -152,6 +152,7 @@ struct ExecContext { + + CPUSet cpu_set; + NUMAPolicy numa_policy; ++ bool cpu_affinity_from_numa; + + ExecInput std_input; + ExecOutput std_output; +@@ -375,6 +376,8 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, + void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds); + void exec_runtime_vacuum(Manager *m); + ++bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c); ++ + const char* exec_output_to_string(ExecOutput i) _const_; + ExecOutput exec_output_from_string(const char *s) _pure_; + +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 33fdb82754..740401a582 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1251,13 +1251,25 @@ int config_parse_exec_cpu_affinity(const char *unit, + void *userdata) { + + ExecContext *c = data; ++ int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + +- return parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue); ++ if (streq(rvalue, "numa")) { ++ c->cpu_affinity_from_numa = true; ++ cpu_set_reset(&c->cpu_set); ++ ++ return 0; ++ } ++ ++ r = parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue); ++ if (r >= 0) ++ c->cpu_affinity_from_numa = false; ++ ++ return r; + } + + int config_parse_capability_set( +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 7029aa5615..daa2c2dce5 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -26,6 +26,8 @@ + #include "securebits-util.h" + #include "signal-util.h" + #include "socket-protocol-list.h" ++#include "socket-util.h" ++#include "stdio-util.h" + #include "string-util.h" + #include "syslog-util.h" + #include "terminal-util.h" +@@ -997,6 +999,13 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + _cleanup_free_ uint8_t *array = NULL; + size_t allocated; + ++ if (eq && streq(eq, "numa")) { ++ r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true); ++ if (r < 0) ++ return bus_log_create_error(r); ++ return r; ++ } ++ + r = parse_cpu_set(eq, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 136eaca82d..1b7be5df4e 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -218,12 +218,12 @@ static void test_parse_cpu_set_extend(void) { + + log_info("/* %s */", __func__); + +- assert_se(parse_cpu_set_extend("1 3", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(parse_cpu_set_extend("1 3", &c, true, NULL, "fake", 1, "CPUAffinity") == 1); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 2); + assert_se(s1 = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", s1); + +- assert_se(parse_cpu_set_extend("4", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(parse_cpu_set_extend("4", &c, true, NULL, "fake", 1, "CPUAffinity") == 1); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 3); + assert_se(s2 = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", s2); +@@ -240,7 +240,7 @@ static void test_cpu_set_to_from_dbus(void) { + + log_info("/* %s */", __func__); + +- assert_se(parse_cpu_set_extend("1 3 8 100-200", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(parse_cpu_set_extend("1 3 8 100-200", &c, true, NULL, "fake", 1, "CPUAffinity") == 1); + assert_se(s = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", s); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 104); +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index bffac4ffe6..7ccaa5b412 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -279,6 +279,18 @@ else + # Maks must be ignored + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + ++ echo "Unit file CPUAffinity=NUMA support" ++ writeTestUnitNUMAPolicy "bind" "0" ++ echo "CPUAffinity=numa" >> $testUnitNUMAConf ++ systemctl daemon-reload ++ systemctl start $testUnit ++ systemctlCheckNUMAProperties $testUnit "bind" "0" ++ pid=$(systemctl show --value -p MainPID $testUnit) ++ cpulist=$(cat /sys/devices/system/node/node0/cpulist) ++ affinity_systemd=$(systemctl show --value -p CPUAffinity $testUnit) ++ [ $cpulist = $affinity_systemd ] ++ pid1StopUnit $testUnit ++ + echo "systemd-run NUMAPolicy support" + runUnit='numa-systemd-run-test.service' + +@@ -309,6 +321,12 @@ else + systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "local" "" + pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "local" "" ++ systemctl cat $runUnit | grep -q 'CPUAffinity=numa' ++ pid1StopUnit $runUnit ++ + fi + + # Cleanup diff --git a/SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch b/SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch new file mode 100644 index 0000000..59daa5f --- /dev/null +++ b/SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch @@ -0,0 +1,93 @@ +From 57d2e6e64ba490054f8de1a2aad4ffae7778eddc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 31 May 2020 18:21:09 +0200 +Subject: [PATCH] basic/user-util: always use base 10 for user/group numbers + +We would parse numbers with base prefixes as user identifiers. For example, +"0x2b3bfa0" would be interpreted as UID==45334432 and "01750" would be +interpreted as UID==1000. This parsing was used also in cases where either a +user/group name or number may be specified. This means that names like +0x2b3bfa0 would be ambiguous: they are a valid user name according to our +documented relaxed rules, but they would also be parsed as numeric uids. + +This behaviour is definitely not expected by users, since tools generally only +accept decimal numbers (e.g. id, getent passwd), while other tools only accept +user names and thus will interpret such strings as user names without even +attempting to convert them to numbers (su, ssh). So let's follow suit and only +accept numbers in decimal notation. Effectively this means that we will reject +such strings as a username/uid/groupname/gid where strict mode is used, and try +to look up a user/group with such a name in relaxed mode. + +Since the function changed is fairly low-level and fairly widely used, this +affects multiple tools: loginctl show-user/enable-linger/disable-linger foo', +the third argument in sysusers.d, fourth and fifth arguments in tmpfiles.d, +etc. + +Fixes #15985. + +(cherry picked from commit 156a5fd297b61bce31630d7a52c15614bf784843) + +Resolves: #1848373 +--- + src/basic/parse-util.h | 8 ++++++-- + src/basic/user-util.c | 2 +- + src/test/test-user-util.c | 10 ++++++++++ + 3 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h +index f3267f4cfe..1fc1af7615 100644 +--- a/src/basic/parse-util.h ++++ b/src/basic/parse-util.h +@@ -50,9 +50,13 @@ static inline int safe_atoux16(const char *s, uint16_t *ret) { + + int safe_atoi16(const char *s, int16_t *ret); + +-static inline int safe_atou32(const char *s, uint32_t *ret_u) { ++static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) { + assert_cc(sizeof(uint32_t) == sizeof(unsigned)); +- return safe_atou(s, (unsigned*) ret_u); ++ return safe_atou_full(s, base, (unsigned*) ret_u); ++} ++ ++static inline int safe_atou32(const char *s, uint32_t *ret_u) { ++ return safe_atou32_full(s, 0, (unsigned*) ret_u); + } + + static inline int safe_atoi32(const char *s, int32_t *ret_i) { +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index d92969c966..10eeb256cd 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -49,7 +49,7 @@ int parse_uid(const char *s, uid_t *ret) { + assert(s); + + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +- r = safe_atou32(s, &uid); ++ r = safe_atou32_full(s, 10, &uid); + if (r < 0) + return r; + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 9114d30b8c..8bf3dcd567 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -46,9 +46,19 @@ static void test_parse_uid(void) { + + r = parse_uid("65535", &uid); + assert_se(r == -ENXIO); ++ assert_se(uid == 100); ++ ++ r = parse_uid("0x1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("01234", &uid); ++ assert_se(r == 0); ++ assert_se(uid == 1234); + + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); ++ assert_se(uid == 1234); + } + + static void test_uid_ptr(void) { diff --git a/SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch b/SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch new file mode 100644 index 0000000..7d41bb9 --- /dev/null +++ b/SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch @@ -0,0 +1,149 @@ +From 2fd9a21a8b6a93c4fb2747839766adca15faa008 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 14 Nov 2019 14:49:40 +0100 +Subject: [PATCH] parse-util: sometimes it is useful to check if a string is a + valid integer, but not actually parse it + +(cherry picked from commit 22810041c2200fe72b0e0c985d0e404f8b80f9e2) + +Related: #1848373 +--- + src/basic/parse-util.c | 34 ++++++++++++++++++++-------------- + 1 file changed, 20 insertions(+), 14 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 6becf85878..056e56765e 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -383,7 +383,6 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + unsigned long l; + + assert(s); +- assert(ret_u); + assert(base <= 16); + + /* strtoul() is happy to parse negative values, and silently +@@ -407,7 +406,9 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + if ((unsigned long) (unsigned) l != l) + return -ERANGE; + +- *ret_u = (unsigned) l; ++ if (ret_u) ++ *ret_u = (unsigned) l; ++ + return 0; + } + +@@ -416,7 +417,6 @@ int safe_atoi(const char *s, int *ret_i) { + long l; + + assert(s); +- assert(ret_i); + + errno = 0; + l = strtol(s, &x, 0); +@@ -427,7 +427,9 @@ int safe_atoi(const char *s, int *ret_i) { + if ((long) (int) l != l) + return -ERANGE; + +- *ret_i = (int) l; ++ if (ret_i) ++ *ret_i = (int) l; ++ + return 0; + } + +@@ -436,7 +438,6 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { + unsigned long long l; + + assert(s); +- assert(ret_llu); + + s += strspn(s, WHITESPACE); + +@@ -449,7 +450,9 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { + if (*s == '-') + return -ERANGE; + +- *ret_llu = l; ++ if (ret_llu) ++ *ret_llu = l; ++ + return 0; + } + +@@ -458,7 +461,6 @@ int safe_atolli(const char *s, long long int *ret_lli) { + long long l; + + assert(s); +- assert(ret_lli); + + errno = 0; + l = strtoll(s, &x, 0); +@@ -467,7 +469,9 @@ int safe_atolli(const char *s, long long int *ret_lli) { + if (!x || x == s || *x != 0) + return -EINVAL; + +- *ret_lli = l; ++ if (ret_lli) ++ *ret_lli = l; ++ + return 0; + } + +@@ -476,7 +480,6 @@ int safe_atou8(const char *s, uint8_t *ret) { + unsigned long l; + + assert(s); +- assert(ret); + + s += strspn(s, WHITESPACE); + +@@ -491,7 +494,8 @@ int safe_atou8(const char *s, uint8_t *ret) { + if ((unsigned long) (uint8_t) l != l) + return -ERANGE; + +- *ret = (uint8_t) l; ++ if (ret) ++ *ret = (uint8_t) l; + return 0; + } + +@@ -525,7 +529,6 @@ int safe_atoi16(const char *s, int16_t *ret) { + long l; + + assert(s); +- assert(ret); + + errno = 0; + l = strtol(s, &x, 0); +@@ -536,7 +539,9 @@ int safe_atoi16(const char *s, int16_t *ret) { + if ((long) (int16_t) l != l) + return -ERANGE; + +- *ret = (int16_t) l; ++ if (ret) ++ *ret = (int16_t) l; ++ + return 0; + } + +@@ -546,7 +551,6 @@ int safe_atod(const char *s, double *ret_d) { + double d = 0; + + assert(s); +- assert(ret_d); + + loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); + if (loc == (locale_t) 0) +@@ -559,7 +563,9 @@ int safe_atod(const char *s, double *ret_d) { + if (!x || x == s || *x != 0) + return -EINVAL; + +- *ret_d = (double) d; ++ if (ret_d) ++ *ret_d = (double) d; ++ + return 0; + } + diff --git a/SOURCES/0460-basic-parse-util-add-safe_atoux64.patch b/SOURCES/0460-basic-parse-util-add-safe_atoux64.patch new file mode 100644 index 0000000..0405582 --- /dev/null +++ b/SOURCES/0460-basic-parse-util-add-safe_atoux64.patch @@ -0,0 +1,130 @@ +From bd47a98d3ce2c5e1d74deb7bc384e416a9070b96 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 9 Apr 2020 11:18:26 +0200 +Subject: [PATCH] basic/parse-util: add safe_atoux64() + +(cherry picked from commit ce51632a357d347737bf40d3817df331cd8874cb) + +Related: #1848373 +--- + src/basic/parse-util.c | 4 ++-- + src/basic/parse-util.h | 12 +++++++++++- + src/test/test-parse-util.c | 39 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 52 insertions(+), 3 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 056e56765e..67056c0434 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -433,7 +433,7 @@ int safe_atoi(const char *s, int *ret_i) { + return 0; + } + +-int safe_atollu(const char *s, long long unsigned *ret_llu) { ++int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { + char *x = NULL; + unsigned long long l; + +@@ -442,7 +442,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { + s += strspn(s, WHITESPACE); + + errno = 0; +- l = strtoull(s, &x, 0); ++ l = strtoull(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h +index 1fc1af7615..8a49257050 100644 +--- a/src/basic/parse-util.h ++++ b/src/basic/parse-util.h +@@ -33,7 +33,6 @@ static inline int safe_atou(const char *s, unsigned *ret_u) { + } + + int safe_atoi(const char *s, int *ret_i); +-int safe_atollu(const char *s, unsigned long long *ret_u); + int safe_atolli(const char *s, long long int *ret_i); + + int safe_atou8(const char *s, uint8_t *ret); +@@ -64,6 +63,12 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { + return safe_atoi(s, (int*) ret_i); + } + ++int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); ++ ++static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { ++ return safe_atollu_full(s, 0, ret_llu); ++} ++ + static inline int safe_atou64(const char *s, uint64_t *ret_u) { + assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +@@ -74,6 +79,11 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { + return safe_atolli(s, (long long int*) ret_i); + } + ++static inline int safe_atoux64(const char *s, uint64_t *ret) { ++ assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); ++ return safe_atollu_full(s, 16, (long long unsigned*) ret); ++} ++ + #if LONG_MAX == INT_MAX + static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned)); +diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c +index e9aef5e882..8b182d9bdc 100644 +--- a/src/test/test-parse-util.c ++++ b/src/test/test-parse-util.c +@@ -561,6 +561,44 @@ static void test_safe_atoi64(void) { + assert_se(r == -EINVAL); + } + ++static void test_safe_atoux64(void) { ++ int r; ++ uint64_t l; ++ ++ r = safe_atoux64("12345", &l); ++ assert_se(r == 0); ++ assert_se(l == 0x12345); ++ ++ r = safe_atoux64(" 12345", &l); ++ assert_se(r == 0); ++ assert_se(l == 0x12345); ++ ++ r = safe_atoux64("0x12345", &l); ++ assert_se(r == 0); ++ assert_se(l == 0x12345); ++ ++ r = safe_atoux64("18446744073709551617", &l); ++ assert_se(r == -ERANGE); ++ ++ r = safe_atoux64("-1", &l); ++ assert_se(r == -ERANGE); ++ ++ r = safe_atoux64(" -1", &l); ++ assert_se(r == -ERANGE); ++ ++ r = safe_atoux64("junk", &l); ++ assert_se(r == -EINVAL); ++ ++ r = safe_atoux64("123x", &l); ++ assert_se(r == -EINVAL); ++ ++ r = safe_atoux64("12.3", &l); ++ assert_se(r == -EINVAL); ++ ++ r = safe_atoux64("", &l); ++ assert_se(r == -EINVAL); ++} ++ + static void test_safe_atod(void) { + int r; + double d; +@@ -836,6 +874,7 @@ int main(int argc, char *argv[]) { + test_safe_atoux16(); + test_safe_atou64(); + test_safe_atoi64(); ++ test_safe_atoux64(); + test_safe_atod(); + test_parse_percent(); + test_parse_percent_unbounded(); diff --git a/SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch b/SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch new file mode 100644 index 0000000..3e97f8b --- /dev/null +++ b/SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch @@ -0,0 +1,137 @@ +From 1d11e79fefea34b4395043e8e951414c5b7817ba Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:06:19 +0200 +Subject: [PATCH] parse-util: allow tweaking how to parse integers + +This allows disabling a few alternative ways to decode integers +formatted as strings, for safety reasons. + +See: #15991 +(cherry picked from commit 707e93aff8f358f8a62117e54b857530d6594e4b) + +Related: #1848373 +--- + src/basic/parse-util.c | 65 +++++++++++++++++++++++++++++++++--------- + src/basic/parse-util.h | 6 ++++ + 2 files changed, 58 insertions(+), 13 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 67056c0434..6cc4fc3e57 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -383,20 +383,35 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + unsigned long l; + + assert(s); +- assert(base <= 16); ++ assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + +- /* strtoul() is happy to parse negative values, and silently +- * converts them to unsigned values without generating an +- * error. We want a clean error, hence let's look for the "-" +- * prefix on our own, and generate an error. But let's do so +- * only after strtoul() validated that the string is clean +- * otherwise, so that we return EINVAL preferably over +- * ERANGE. */ ++ /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without ++ * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and ++ * generate an error. But let's do so only after strtoul() validated that the string is clean ++ * otherwise, so that we return EINVAL preferably over ERANGE. */ ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && ++ strchr(WHITESPACE, s[0])) ++ return -EINVAL; + + s += strspn(s, WHITESPACE); + ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && ++ IN_SET(s[0], '+', '-')) ++ return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a ++ * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we ++ * blanket refuse +/- prefixed integers, while if it is missing we'll just ++ * return ERANGE, because the string actually parses correctly, but doesn't ++ * fit in the return type. */ ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && ++ s[0] == '0' && !streq(s, "0")) ++ return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal ++ * notation and assumed-to-be-decimal integers with a leading zero. */ ++ + errno = 0; +- l = strtoul(s, &x, base); ++ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual ++ * base is left */); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -438,11 +453,24 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + unsigned long long l; + + assert(s); ++ assert(SAFE_ATO_MASK_FLAGS(base) <= 16); ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && ++ strchr(WHITESPACE, s[0])) ++ return -EINVAL; + + s += strspn(s, WHITESPACE); + ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && ++ IN_SET(s[0], '+', '-')) ++ return -EINVAL; ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && ++ s[0] == '0' && s[1] != 0) ++ return -EINVAL; ++ + errno = 0; +- l = strtoull(s, &x, base); ++ l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -504,13 +532,24 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + unsigned long l; + + assert(s); +- assert(ret); +- assert(base <= 16); ++ assert(SAFE_ATO_MASK_FLAGS(base) <= 16); ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && ++ strchr(WHITESPACE, s[0])) ++ return -EINVAL; + + s += strspn(s, WHITESPACE); + ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && ++ IN_SET(s[0], '+', '-')) ++ return -EINVAL; ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && ++ s[0] == '0' && s[1] != 0) ++ return -EINVAL; ++ + errno = 0; +- l = strtoul(s, &x, base); ++ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h +index 8a49257050..c6bbc98dff 100644 +--- a/src/basic/parse-util.h ++++ b/src/basic/parse-util.h +@@ -26,6 +26,12 @@ int parse_syscall_and_errno(const char *in, char **name, int *error); + #define FORMAT_BYTES_MAX 8 + char *format_bytes(char *buf, size_t l, uint64_t t); + ++#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) ++#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) ++#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) ++#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) ++#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) ++ + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); + + static inline int safe_atou(const char *s, unsigned *ret_u) { diff --git a/SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch b/SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch new file mode 100644 index 0000000..29884dc --- /dev/null +++ b/SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch @@ -0,0 +1,60 @@ +From 1c8e5070d8a88f35b5577e091de66727fa785ef7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:08:38 +0200 +Subject: [PATCH] parse-util: allow '-0' as alternative to '0' and '+0' + +Let's allow "-0" as alternative to "+0" and "0" when parsing integers, +unless the new SAFE_ATO_REFUSE_PLUS_MINUS flag is specified. + +In cases where allowing the +/- syntax shall not be allowed +SAFE_ATO_REFUSE_PLUS_MINUS is the right flag to use, but this also means +that -0 as only negative integer that fits into an unsigned value should +be acceptable if the flag is not specified. + +(cherry picked from commit c78eefc13562a8fc0c22c00a6d3001af89860258) + +Related: #1848373 +--- + src/basic/parse-util.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 6cc4fc3e57..53d181dd60 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -416,7 +416,7 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (s[0] == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + if ((unsigned long) (unsigned) l != l) + return -ERANGE; +@@ -475,7 +475,7 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (*s == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + + if (ret_llu) +@@ -517,7 +517,7 @@ int safe_atou8(const char *s, uint8_t *ret) { + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (s[0] == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + if ((unsigned long) (uint8_t) l != l) + return -ERANGE; +@@ -554,7 +554,7 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (s[0] == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + if ((unsigned long) (uint16_t) l != l) + return -ERANGE; diff --git a/SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch b/SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch new file mode 100644 index 0000000..8cb4a65 --- /dev/null +++ b/SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch @@ -0,0 +1,31 @@ +From 91ed5edcdea79773f6918e739637521e47129b07 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:10:27 +0200 +Subject: [PATCH] parse-util: make return parameter optional in + safe_atou16_full() + +All other safe_atoXYZ_full() functions have the parameter optional, +let's make it optoinal here, too. + +(cherry picked from commit aa85e4d3cef8ca8436e480bce9fa4ce72876b636) + +Related: #1848373 +--- + src/basic/parse-util.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 53d181dd60..7a7cefe6ff 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -559,7 +559,9 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + if ((unsigned long) (uint16_t) l != l) + return -ERANGE; + +- *ret = (uint16_t) l; ++ if (ret) ++ *ret = (uint16_t) l; ++ + return 0; + } + diff --git a/SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch b/SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch new file mode 100644 index 0000000..318e18b --- /dev/null +++ b/SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch @@ -0,0 +1,59 @@ +From 147a3696b45a872e0e21fb74e1497f02543ce871 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:16:04 +0200 +Subject: [PATCH] parse-util: rewrite parse_mode() on top of safe_atou_full() + +Parsing is hard, hence let's use our own careful wrappers wherever +possible. + +(cherry picked from commit c44702a8bd8cc8b7f2f1df21db9308d9af7dda5b) + +Related: #1848373 +--- + src/basic/parse-util.c | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 7a7cefe6ff..68c156c543 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -54,26 +54,24 @@ int parse_pid(const char *s, pid_t* ret_pid) { + } + + int parse_mode(const char *s, mode_t *ret) { +- char *x; +- long l; ++ unsigned m; ++ int r; + + assert(s); +- assert(ret); + +- s += strspn(s, WHITESPACE); +- if (s[0] == '-') +- return -ERANGE; +- +- errno = 0; +- l = strtol(s, &x, 8); +- if (errno > 0) +- return -errno; +- if (!x || x == s || *x != 0) +- return -EINVAL; +- if (l < 0 || l > 07777) ++ r = safe_atou_full(s, 8 | ++ SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird, ++ * refuse. User might have wanted to add mode flags or ++ * so, but this parser doesn't allow that, so let's ++ * better be safe. */ ++ &m); ++ if (r < 0) ++ return r; ++ if (m > 07777) + return -ERANGE; + +- *ret = (mode_t) l; ++ if (ret) ++ *ret = m; + return 0; + } + diff --git a/SOURCES/0465-user-util-be-stricter-in-parse_uid.patch b/SOURCES/0465-user-util-be-stricter-in-parse_uid.patch new file mode 100644 index 0000000..79cb570 --- /dev/null +++ b/SOURCES/0465-user-util-be-stricter-in-parse_uid.patch @@ -0,0 +1,78 @@ +From 87c22d3bb794118d25bc138108fd5bdd607365ef Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:16:46 +0200 +Subject: [PATCH] user-util: be stricter in parse_uid() + +Let's refuse "+" and "-" prefixed UIDs. Let's refuse whitespace-prefixed +UIDS, Let's refuse zero-prefixed UIDs. Let's be safe than sorry. + +(cherry picked from commit f5979b63cc305ba217dfd174b1bf0583bcf75a73) + +Related: #1848373 +--- + src/basic/user-util.c | 10 +++++++++- + src/test/test-user-util.c | 26 +++++++++++++++++++++++--- + 2 files changed, 32 insertions(+), 4 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 10eeb256cd..40f4e45db6 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -49,7 +49,15 @@ int parse_uid(const char *s, uid_t *ret) { + assert(s); + + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +- r = safe_atou32_full(s, 10, &uid); ++ ++ /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and ++ * whitespace. We do this, since this call is often used in a context where we parse things as UID ++ * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs ++ * are parsed as UIDs only if they really really look like UIDs. */ ++ r = safe_atou32_full(s, 10 ++ | SAFE_ATO_REFUSE_PLUS_MINUS ++ | SAFE_ATO_REFUSE_LEADING_ZERO ++ | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid); + if (r < 0) + return r; + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 8bf3dcd567..99203f7e48 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -52,13 +52,33 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("+1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("-1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid(" 1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("01234", &uid); +- assert_se(r == 0); +- assert_se(uid == 1234); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("-0", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("+0", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); + + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); +- assert_se(uid == 1234); ++ assert_se(uid == 100); + } + + static void test_uid_ptr(void) { diff --git a/SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch b/SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch new file mode 100644 index 0000000..58a4b5a --- /dev/null +++ b/SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch @@ -0,0 +1,75 @@ +From 50b103a982dfd6f1b2bf98bbc98a8063fa153e89 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 23 Nov 2018 16:27:15 +0100 +Subject: [PATCH] strv: add new macro STARTSWITH_SET() + +This is to startswith() what PATH_STARTSWITH_SET() is to +path_startswith(). + +Or in other words, checks if the specified string has any of the listed +prefixes, and if so, returns the remainder of the string. + +(cherry picked from commit 52f1552073047195d51901f7e5a5a4fa3189034e) + +Related: #1848373 +--- + src/basic/strv.h | 12 ++++++++++++ + src/test/test-strv.c | 15 +++++++++++++++ + 2 files changed, 27 insertions(+) + +diff --git a/src/basic/strv.h b/src/basic/strv.h +index 51d03db940..c1e4c973b6 100644 +--- a/src/basic/strv.h ++++ b/src/basic/strv.h +@@ -136,6 +136,18 @@ void strv_print(char **l); + _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ + }) + ++#define STARTSWITH_SET(p, ...) \ ++ ({ \ ++ const char *_p = (p); \ ++ char *_found = NULL, **_i; \ ++ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ ++ _found = startswith(_p, *_i); \ ++ if (_found) \ ++ break; \ ++ } \ ++ _found; \ ++ }) ++ + #define FOREACH_STRING(x, ...) \ + for (char **_l = ({ \ + char **_ll = STRV_MAKE(__VA_ARGS__); \ +diff --git a/src/test/test-strv.c b/src/test/test-strv.c +index 1c192239a2..79d999d3ed 100644 +--- a/src/test/test-strv.c ++++ b/src/test/test-strv.c +@@ -56,6 +56,20 @@ static void test_strptr_in_set(void) { + assert_se(!STRPTR_IN_SET(NULL, NULL)); + } + ++static void test_startswith_set(void) { ++ assert_se(!STARTSWITH_SET("foo", "bar", "baz", "waldo")); ++ assert_se(!STARTSWITH_SET("foo", "bar")); ++ ++ assert_se(STARTSWITH_SET("abc", "a", "ab", "abc")); ++ assert_se(STARTSWITH_SET("abc", "ax", "ab", "abc")); ++ assert_se(STARTSWITH_SET("abc", "ax", "abx", "abc")); ++ assert_se(!STARTSWITH_SET("abc", "ax", "abx", "abcx")); ++ ++ assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar")); ++ assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar")); ++ assert_se(streq_ptr(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), "")); ++} ++ + static const char* const input_table_multiple[] = { + "one", + "two", +@@ -700,6 +714,7 @@ int main(int argc, char *argv[]) { + test_specifier_printf(); + test_str_in_set(); + test_strptr_in_set(); ++ test_startswith_set(); + test_strv_foreach(); + test_strv_foreach_backwards(); + test_strv_foreach_pair(); diff --git a/SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch b/SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch new file mode 100644 index 0000000..d088a5a --- /dev/null +++ b/SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch @@ -0,0 +1,164 @@ +From e67e29d91a1ef90af545e4130c7b4c4cfde6202a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:31:51 +0200 +Subject: [PATCH] parse-util: also parse integers prefixed with 0b and 0o + +Let's adopt Python 3 style 0b and 0x syntaxes, because it makes a ton of +sense, in particular in bitmask settings. + +(cherry picked from commit fc80cabcf584a8b486bdff5be0c074fec4059cdc) + +Related: #1848373 +--- + src/basic/parse-util.c | 56 ++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 51 insertions(+), 5 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 68c156c543..992ea3605b 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -17,6 +17,7 @@ + #include "parse-util.h" + #include "process-util.h" + #include "string-util.h" ++#include "strv.h" + + int parse_boolean(const char *v) { + assert(v); +@@ -373,7 +374,32 @@ char *format_bytes(char *buf, size_t l, uint64_t t) { + finish: + buf[l-1] = 0; + return buf; ++} ++ ++static const char *mangle_base(const char *s, unsigned *base) { ++ const char *k; ++ ++ assert(s); ++ assert(base); ++ ++ /* Base already explicitly specified, then don't do anything. */ ++ if (SAFE_ATO_MASK_FLAGS(*base) != 0) ++ return s; + ++ /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */ ++ k = STARTSWITH_SET(s, "0b", "0B"); ++ if (k) { ++ *base = 2 | (*base & SAFE_ATO_ALL_FLAGS); ++ return k; ++ } ++ ++ k = STARTSWITH_SET(s, "0o", "0O"); ++ if (k) { ++ *base = 8 | (*base & SAFE_ATO_ALL_FLAGS); ++ return k; ++ } ++ ++ return s; + } + + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { +@@ -407,6 +433,8 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal + * notation and assumed-to-be-decimal integers with a leading zero. */ + ++ s = mangle_base(s, &base); ++ + errno = 0; + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual + * base is left */); +@@ -426,13 +454,17 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + } + + int safe_atoi(const char *s, int *ret_i) { ++ unsigned base = 0; + char *x = NULL; + long l; + + assert(s); + ++ s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); ++ + errno = 0; +- l = strtol(s, &x, 0); ++ l = strtol(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -467,6 +499,8 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + s[0] == '0' && s[1] != 0) + return -EINVAL; + ++ s = mangle_base(s, &base); ++ + errno = 0; + l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) +@@ -483,13 +517,17 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + } + + int safe_atolli(const char *s, long long int *ret_lli) { ++ unsigned base = 0; + char *x = NULL; + long long l; + + assert(s); + ++ s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); ++ + errno = 0; +- l = strtoll(s, &x, 0); ++ l = strtoll(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -502,15 +540,17 @@ int safe_atolli(const char *s, long long int *ret_lli) { + } + + int safe_atou8(const char *s, uint8_t *ret) { +- char *x = NULL; ++ unsigned base = 0; + unsigned long l; ++ char *x = NULL; + + assert(s); + + s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); + + errno = 0; +- l = strtoul(s, &x, 0); ++ l = strtoul(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -546,6 +586,8 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + s[0] == '0' && s[1] != 0) + return -EINVAL; + ++ s = mangle_base(s, &base); ++ + errno = 0; + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) +@@ -564,13 +606,17 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + } + + int safe_atoi16(const char *s, int16_t *ret) { ++ unsigned base = 0; + char *x = NULL; + long l; + + assert(s); + ++ s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); ++ + errno = 0; +- l = strtol(s, &x, 0); ++ l = strtol(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) diff --git a/SOURCES/0468-tests-beef-up-integer-parsing-tests.patch b/SOURCES/0468-tests-beef-up-integer-parsing-tests.patch new file mode 100644 index 0000000..c33c38f --- /dev/null +++ b/SOURCES/0468-tests-beef-up-integer-parsing-tests.patch @@ -0,0 +1,204 @@ +From 457eada27f606e39f0efc6adc226542fd11eb815 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:48:41 +0200 +Subject: [PATCH] tests: beef up integer parsing tests + +(cherry picked from commit 53c6db99fa4b52f97e19977f21d3133f8ceb3dcd) + +Related: #1848373 +--- + src/test/test-parse-util.c | 58 ++++++++++++++++++++++++++++++++++++++ + src/test/test-user-util.c | 40 ++++++++++++++++++++++++++ + 2 files changed, 98 insertions(+) + +diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c +index 8b182d9bdc..699499b665 100644 +--- a/src/test/test-parse-util.c ++++ b/src/test/test-parse-util.c +@@ -75,14 +75,22 @@ static void test_parse_mode(void) { + mode_t m; + + assert_se(parse_mode("-1", &m) < 0); ++ assert_se(parse_mode("+1", &m) < 0); + assert_se(parse_mode("", &m) < 0); + assert_se(parse_mode("888", &m) < 0); + assert_se(parse_mode("77777", &m) < 0); + + assert_se(parse_mode("544", &m) >= 0 && m == 0544); ++ assert_se(parse_mode("0544", &m) >= 0 && m == 0544); ++ assert_se(parse_mode("00544", &m) >= 0 && m == 0544); + assert_se(parse_mode("777", &m) >= 0 && m == 0777); ++ assert_se(parse_mode("0777", &m) >= 0 && m == 0777); ++ assert_se(parse_mode("00777", &m) >= 0 && m == 0777); + assert_se(parse_mode("7777", &m) >= 0 && m == 07777); ++ assert_se(parse_mode("07777", &m) >= 0 && m == 07777); ++ assert_se(parse_mode("007777", &m) >= 0 && m == 07777); + assert_se(parse_mode("0", &m) >= 0 && m == 0); ++ assert_se(parse_mode(" 1", &m) >= 0 && m == 1); + } + + static void test_parse_size(void) { +@@ -358,6 +366,18 @@ static void test_safe_atolli(void) { + assert_se(r == 0); + assert_se(l == -12345); + ++ r = safe_atolli("0x5", &l); ++ assert_se(r == 0); ++ assert_se(l == 5); ++ ++ r = safe_atolli("0o6", &l); ++ assert_se(r == 0); ++ assert_se(l == 6); ++ ++ r = safe_atolli("0B101", &l); ++ assert_se(r == 0); ++ assert_se(l == 5); ++ + r = safe_atolli("12345678901234567890", &l); + assert_se(r == -ERANGE); + +@@ -431,6 +451,14 @@ static void test_safe_atoi16(void) { + assert_se(r == 0); + assert_se(l == 32767); + ++ r = safe_atoi16("0o11", &l); ++ assert_se(r == 0); ++ assert_se(l == 9); ++ ++ r = safe_atoi16("0B110", &l); ++ assert_se(r == 0); ++ assert_se(l == 6); ++ + r = safe_atoi16("36536", &l); + assert_se(r == -ERANGE); + +@@ -475,6 +503,13 @@ static void test_safe_atoux16(void) { + r = safe_atoux16(" -1", &l); + assert_se(r == -ERANGE); + ++ r = safe_atoux16("0b1", &l); ++ assert_se(r == 0); ++ assert_se(l == 177); ++ ++ r = safe_atoux16("0o70", &l); ++ assert_se(r == -EINVAL); ++ + r = safe_atoux16("junk", &l); + assert_se(r == -EINVAL); + +@@ -500,6 +535,14 @@ static void test_safe_atou64(void) { + assert_se(r == 0); + assert_se(l == 12345); + ++ r = safe_atou64("0o11", &l); ++ assert_se(r == 0); ++ assert_se(l == 9); ++ ++ r = safe_atou64("0b11", &l); ++ assert_se(r == 0); ++ assert_se(l == 3); ++ + r = safe_atou64("18446744073709551617", &l); + assert_se(r == -ERANGE); + +@@ -542,6 +585,14 @@ static void test_safe_atoi64(void) { + assert_se(r == 0); + assert_se(l == 32767); + ++ r = safe_atoi64(" 0o20", &l); ++ assert_se(r == 0); ++ assert_se(l == 16); ++ ++ r = safe_atoi64(" 0b01010", &l); ++ assert_se(r == 0); ++ assert_se(l == 10); ++ + r = safe_atoi64("9223372036854775813", &l); + assert_se(r == -ERANGE); + +@@ -577,6 +628,13 @@ static void test_safe_atoux64(void) { + assert_se(r == 0); + assert_se(l == 0x12345); + ++ r = safe_atoux64("0b11011", &l); ++ assert_se(r == 0); ++ assert_se(l == 11603985); ++ ++ r = safe_atoux64("0o11011", &l); ++ assert_se(r == -EINVAL); ++ + r = safe_atoux64("18446744073709551617", &l); + assert_se(r == -ERANGE); + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 99203f7e48..04e86f5ac3 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -40,6 +40,22 @@ static void test_parse_uid(void) { + + log_info("/* %s */", __func__); + ++ r = parse_uid("0", &uid); ++ assert_se(r == 0); ++ assert_se(uid == 0); ++ ++ r = parse_uid("1", &uid); ++ assert_se(r == 0); ++ assert_se(uid == 1); ++ ++ r = parse_uid("01", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 1); ++ ++ r = parse_uid("001", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 1); ++ + r = parse_uid("100", &uid); + assert_se(r == 0); + assert_se(uid == 100); +@@ -52,6 +68,14 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("0o1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("0b1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("+1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); +@@ -68,6 +92,14 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("001234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("0001234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("-0", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); +@@ -76,6 +108,14 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("00", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("000", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); diff --git a/SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch b/SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch new file mode 100644 index 0000000..eefc47b --- /dev/null +++ b/SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch @@ -0,0 +1,270 @@ +From 1e4ec1b29d15684a305bbc9ab54c6c8321504e7b Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 27 Oct 2020 10:31:05 +0100 +Subject: [PATCH] shared/user-util: add compat forms of user name checking + functions + +New functions are called valid_user_group_name_compat() and +valid_user_group_name_or_id_compat() and accept dots in the user +or group name. No functional change except the tests. + +(cherry picked from commit 1a29610f5fa1bcb2eeb37d2c6b79d8d1a6dbb865) + +This completes previous partial cherry-pick of the same commit (commit +76176de0889c3e8b9b3a176da24e4f8dbbd380a3). + +Related: #1848373 +--- + src/basic/user-util.c | 32 +++++++------- + src/basic/user-util.h | 16 ++++++- + src/test/test-user-util.c | 91 +++++++++++++++++++++++++++++++++++++-- + 3 files changed, 117 insertions(+), 22 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 40f4e45db6..03cbbc2503 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -576,7 +576,7 @@ int take_etc_passwd_lock(const char *root) { + return fd; + } + +-bool valid_user_group_name(const char *u) { ++bool valid_user_group_name_full(const char *u, bool strict) { + const char *i; + long sz; + +@@ -585,12 +585,12 @@ bool valid_user_group_name(const char *u) { + * + * - We require that names fit into the appropriate utmp field + * - We don't allow empty user names ++ * - No dots or digits in the first character + * +- * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. ++ * If strict==true, additionally: ++ * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) + * +- * jsynacek: We now allow dots in user names. The checks are not exhaustive as user names like "..." are allowed +- * and valid according to POSIX, but can't be created using useradd. However, ".user" can be created. Let's not +- * complicate the code by adding additional checks for weird corner cases like these, as they don't really matter here. ++ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. + */ + + if (isempty(u)) +@@ -598,16 +598,16 @@ bool valid_user_group_name(const char *u) { + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && +- u[0] != '_' && u[0] != '.') ++ u[0] != '_') + return false; + +- for (i = u+1; *i; i++) { +- if (!(*i >= 'a' && *i <= 'z') && +- !(*i >= 'A' && *i <= 'Z') && +- !(*i >= '0' && *i <= '9') && +- !IN_SET(*i, '_', '-', '.')) ++ for (i = u+1; *i; i++) ++ if (!((*i >= 'a' && *i <= 'z') || ++ (*i >= 'A' && *i <= 'Z') || ++ (*i >= '0' && *i <= '9') || ++ IN_SET(*i, '_', '-') || ++ (!strict && *i == '.'))) + return false; +- } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); +@@ -621,15 +621,15 @@ bool valid_user_group_name(const char *u) { + return true; + } + +-bool valid_user_group_name_or_id(const char *u) { ++bool valid_user_group_name_or_id_full(const char *u, bool strict) { + +- /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right +- * range, and not the invalid user ids. */ ++ /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the ++ * right range, and not the invalid user ids. */ + + if (isempty(u)) + return false; + +- if (valid_user_group_name(u)) ++ if (valid_user_group_name_full(u, strict)) + return true; + + return parse_uid(u, NULL) >= 0; +diff --git a/src/basic/user-util.h b/src/basic/user-util.h +index b74f168859..5ad0b2a2f9 100644 +--- a/src/basic/user-util.h ++++ b/src/basic/user-util.h +@@ -78,8 +78,20 @@ static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; + } + +-bool valid_user_group_name(const char *u); +-bool valid_user_group_name_or_id(const char *u); ++bool valid_user_group_name_full(const char *u, bool strict); ++bool valid_user_group_name_or_id_full(const char *u, bool strict); ++static inline bool valid_user_group_name(const char *u) { ++ return valid_user_group_name_full(u, true); ++} ++static inline bool valid_user_group_name_or_id(const char *u) { ++ return valid_user_group_name_or_id_full(u, true); ++} ++static inline bool valid_user_group_name_compat(const char *u) { ++ return valid_user_group_name_full(u, false); ++} ++static inline bool valid_user_group_name_or_id_compat(const char *u) { ++ return valid_user_group_name_or_id_full(u, false); ++} + bool valid_gecos(const char *d); + bool valid_home(const char *p); + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 04e86f5ac3..3a4211655d 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -131,6 +131,43 @@ static void test_uid_ptr(void) { + assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); + } + ++static void test_valid_user_group_name_compat(void) { ++ log_info("/* %s */", __func__); ++ ++ assert_se(!valid_user_group_name_compat(NULL)); ++ assert_se(!valid_user_group_name_compat("")); ++ assert_se(!valid_user_group_name_compat("1")); ++ assert_se(!valid_user_group_name_compat("65535")); ++ assert_se(!valid_user_group_name_compat("-1")); ++ assert_se(!valid_user_group_name_compat("-kkk")); ++ assert_se(!valid_user_group_name_compat("rööt")); ++ assert_se(!valid_user_group_name_compat(".")); ++ assert_se(!valid_user_group_name_compat(".eff")); ++ assert_se(!valid_user_group_name_compat("foo\nbar")); ++ assert_se(!valid_user_group_name_compat("0123456789012345678901234567890123456789")); ++ assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); ++ assert_se(!valid_user_group_name_compat(".")); ++ assert_se(!valid_user_group_name_compat(".1")); ++ assert_se(!valid_user_group_name_compat(".65535")); ++ assert_se(!valid_user_group_name_compat(".-1")); ++ assert_se(!valid_user_group_name_compat(".-kkk")); ++ assert_se(!valid_user_group_name_compat(".rööt")); ++ assert_se(!valid_user_group_name_or_id_compat(".aaa:bbb")); ++ ++ assert_se(valid_user_group_name_compat("root")); ++ assert_se(valid_user_group_name_compat("lennart")); ++ assert_se(valid_user_group_name_compat("LENNART")); ++ assert_se(valid_user_group_name_compat("_kkk")); ++ assert_se(valid_user_group_name_compat("kkk-")); ++ assert_se(valid_user_group_name_compat("kk-k")); ++ assert_se(valid_user_group_name_compat("eff.eff")); ++ assert_se(valid_user_group_name_compat("eff.")); ++ ++ assert_se(valid_user_group_name_compat("some5")); ++ assert_se(!valid_user_group_name_compat("5some")); ++ assert_se(valid_user_group_name_compat("INNER5NUMBER")); ++} ++ + static void test_valid_user_group_name(void) { + log_info("/* %s */", __func__); + +@@ -141,9 +178,18 @@ static void test_valid_user_group_name(void) { + assert_se(!valid_user_group_name("-1")); + assert_se(!valid_user_group_name("-kkk")); + assert_se(!valid_user_group_name("rööt")); ++ assert_se(!valid_user_group_name(".")); ++ assert_se(!valid_user_group_name(".eff")); + assert_se(!valid_user_group_name("foo\nbar")); + assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); ++ assert_se(!valid_user_group_name(".")); ++ assert_se(!valid_user_group_name(".1")); ++ assert_se(!valid_user_group_name(".65535")); ++ assert_se(!valid_user_group_name(".-1")); ++ assert_se(!valid_user_group_name(".-kkk")); ++ assert_se(!valid_user_group_name(".rööt")); ++ assert_se(!valid_user_group_name_or_id(".aaa:bbb")); + + assert_se(valid_user_group_name("root")); + assert_se(valid_user_group_name("lennart")); +@@ -151,14 +197,47 @@ static void test_valid_user_group_name(void) { + assert_se(valid_user_group_name("_kkk")); + assert_se(valid_user_group_name("kkk-")); + assert_se(valid_user_group_name("kk-k")); +- assert_se(valid_user_group_name(".moo")); +- assert_se(valid_user_group_name("eff.eff")); ++ assert_se(!valid_user_group_name("eff.eff")); ++ assert_se(!valid_user_group_name("eff.")); + + assert_se(valid_user_group_name("some5")); + assert_se(!valid_user_group_name("5some")); + assert_se(valid_user_group_name("INNER5NUMBER")); + } + ++static void test_valid_user_group_name_or_id_compat(void) { ++ log_info("/* %s */", __func__); ++ ++ assert_se(!valid_user_group_name_or_id_compat(NULL)); ++ assert_se(!valid_user_group_name_or_id_compat("")); ++ assert_se(valid_user_group_name_or_id_compat("0")); ++ assert_se(valid_user_group_name_or_id_compat("1")); ++ assert_se(valid_user_group_name_or_id_compat("65534")); ++ assert_se(!valid_user_group_name_or_id_compat("65535")); ++ assert_se(valid_user_group_name_or_id_compat("65536")); ++ assert_se(!valid_user_group_name_or_id_compat("-1")); ++ assert_se(!valid_user_group_name_or_id_compat("-kkk")); ++ assert_se(!valid_user_group_name_or_id_compat("rööt")); ++ assert_se(!valid_user_group_name_or_id_compat(".")); ++ assert_se(!valid_user_group_name_or_id_compat(".eff")); ++ assert_se(valid_user_group_name_or_id_compat("eff.eff")); ++ assert_se(valid_user_group_name_or_id_compat("eff.")); ++ assert_se(!valid_user_group_name_or_id_compat("foo\nbar")); ++ assert_se(!valid_user_group_name_or_id_compat("0123456789012345678901234567890123456789")); ++ assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); ++ ++ assert_se(valid_user_group_name_or_id_compat("root")); ++ assert_se(valid_user_group_name_or_id_compat("lennart")); ++ assert_se(valid_user_group_name_or_id_compat("LENNART")); ++ assert_se(valid_user_group_name_or_id_compat("_kkk")); ++ assert_se(valid_user_group_name_or_id_compat("kkk-")); ++ assert_se(valid_user_group_name_or_id_compat("kk-k")); ++ ++ assert_se(valid_user_group_name_or_id_compat("some5")); ++ assert_se(!valid_user_group_name_or_id_compat("5some")); ++ assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER")); ++} ++ + static void test_valid_user_group_name_or_id(void) { + log_info("/* %s */", __func__); + +@@ -172,6 +251,10 @@ static void test_valid_user_group_name_or_id(void) { + assert_se(!valid_user_group_name_or_id("-1")); + assert_se(!valid_user_group_name_or_id("-kkk")); + assert_se(!valid_user_group_name_or_id("rööt")); ++ assert_se(!valid_user_group_name_or_id(".")); ++ assert_se(!valid_user_group_name_or_id(".eff")); ++ assert_se(!valid_user_group_name_or_id("eff.eff")); ++ assert_se(!valid_user_group_name_or_id("eff.")); + assert_se(!valid_user_group_name_or_id("foo\nbar")); + assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); +@@ -182,8 +265,6 @@ static void test_valid_user_group_name_or_id(void) { + assert_se(valid_user_group_name_or_id("_kkk")); + assert_se(valid_user_group_name_or_id("kkk-")); + assert_se(valid_user_group_name_or_id("kk-k")); +- assert_se(valid_user_group_name_or_id(".moo")); +- assert_se(valid_user_group_name_or_id("eff.eff")); + + assert_se(valid_user_group_name_or_id("some5")); + assert_se(!valid_user_group_name_or_id("5some")); +@@ -286,7 +367,9 @@ int main(int argc, char*argv[]) { + test_parse_uid(); + test_uid_ptr(); + ++ test_valid_user_group_name_compat(); + test_valid_user_group_name(); ++ test_valid_user_group_name_or_id_compat(); + test_valid_user_group_name_or_id(); + test_valid_gecos(); + test_valid_home(); diff --git a/SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch b/SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch new file mode 100644 index 0000000..83422bc --- /dev/null +++ b/SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch @@ -0,0 +1,50 @@ +From fa1fa19951fdadd63f2b5df6224678f91753f260 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 28 Aug 2019 12:05:52 +0200 +Subject: [PATCH] shared/user-util: emit a warning on names with dots + +(cherry picked from commit 88e2ed0b5bf6f08f5a2d4d64b1fefdc7192b9aac) + +Related: #1848373 +--- + src/basic/user-util.c | 27 ++++++++++++++++++++------- + 1 file changed, 20 insertions(+), 7 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 03cbbc2503..359da08a83 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -601,13 +601,26 @@ bool valid_user_group_name_full(const char *u, bool strict) { + u[0] != '_') + return false; + +- for (i = u+1; *i; i++) +- if (!((*i >= 'a' && *i <= 'z') || +- (*i >= 'A' && *i <= 'Z') || +- (*i >= '0' && *i <= '9') || +- IN_SET(*i, '_', '-') || +- (!strict && *i == '.'))) +- return false; ++ bool warned = false; ++ ++ for (i = u+1; *i; i++) { ++ if (((*i >= 'a' && *i <= 'z') || ++ (*i >= 'A' && *i <= 'Z') || ++ (*i >= '0' && *i <= '9') || ++ IN_SET(*i, '_', '-'))) ++ continue; ++ ++ if (*i == '.' && !strict) { ++ if (!warned) { ++ log_warning("Bad user or group name \"%s\", accepting for compatibility.", u); ++ warned = true; ++ } ++ ++ continue; ++ } ++ ++ return false; ++ } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); diff --git a/SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch b/SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch new file mode 100644 index 0000000..52a315a --- /dev/null +++ b/SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch @@ -0,0 +1,103 @@ +From f06434cc51eedd72f7d4a640a1fa118f57a5e68e Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Wed, 18 Mar 2020 18:29:02 +0100 +Subject: [PATCH] user-util: Allow names starting with a digit + +In 1a29610f5fa1bcb2eeb37d2c6b79d8d1a6dbb865 the change inadvertedly +disabled names with digit as the first character. This follow-up change +allows a digit as the first character in compat mode. + +Fixes: #15141 +(cherry picked from commit 93c23c9297e48e594785e0bb9c51504aae5fbe3e) + +Related: #1848373 +--- + src/basic/user-util.c | 20 +++++++++++++++++--- + src/test/test-user-util.c | 4 ++-- + 2 files changed, 19 insertions(+), 5 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 359da08a83..7dd2bb2c84 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -579,16 +579,18 @@ int take_etc_passwd_lock(const char *root) { + bool valid_user_group_name_full(const char *u, bool strict) { + const char *i; + long sz; ++ bool warned = false; + + /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, + * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules: + * + * - We require that names fit into the appropriate utmp field + * - We don't allow empty user names +- * - No dots or digits in the first character ++ * - No dots in the first character + * + * If strict==true, additionally: + * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) ++ * - We don't allow a digit as the first character + * + * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. + */ +@@ -598,17 +600,26 @@ bool valid_user_group_name_full(const char *u, bool strict) { + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && ++ !(u[0] >= '0' && u[0] <= '9' && !strict) && + u[0] != '_') + return false; + +- bool warned = false; ++ bool only_digits_seen = u[0] >= '0' && u[0] <= '9'; ++ ++ if (only_digits_seen) { ++ log_warning("User or group name \"%s\" starts with a digit, accepting for compatibility.", u); ++ warned = true; ++ } + + for (i = u+1; *i; i++) { + if (((*i >= 'a' && *i <= 'z') || + (*i >= 'A' && *i <= 'Z') || + (*i >= '0' && *i <= '9') || +- IN_SET(*i, '_', '-'))) ++ IN_SET(*i, '_', '-'))) { ++ if (!(*i >= '0' && *i <= '9')) ++ only_digits_seen = false; + continue; ++ } + + if (*i == '.' && !strict) { + if (!warned) { +@@ -622,6 +633,9 @@ bool valid_user_group_name_full(const char *u, bool strict) { + return false; + } + ++ if (only_digits_seen) ++ return false; ++ + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 3a4211655d..56079f1486 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -164,7 +164,7 @@ static void test_valid_user_group_name_compat(void) { + assert_se(valid_user_group_name_compat("eff.")); + + assert_se(valid_user_group_name_compat("some5")); +- assert_se(!valid_user_group_name_compat("5some")); ++ assert_se(valid_user_group_name_compat("5some")); + assert_se(valid_user_group_name_compat("INNER5NUMBER")); + } + +@@ -234,7 +234,7 @@ static void test_valid_user_group_name_or_id_compat(void) { + assert_se(valid_user_group_name_or_id_compat("kk-k")); + + assert_se(valid_user_group_name_or_id_compat("some5")); +- assert_se(!valid_user_group_name_or_id_compat("5some")); ++ assert_se(valid_user_group_name_or_id_compat("5some")); + assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER")); + } + diff --git a/SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch b/SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch new file mode 100644 index 0000000..37544d6 --- /dev/null +++ b/SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch @@ -0,0 +1,193 @@ +From 40dff18947fa198810db4cd3e5291349fc84a0e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 1 Aug 2019 10:02:14 +0200 +Subject: [PATCH] shared/user-util: allow usernames with dots in specific + fields + +People do have usernames with dots, and it makes them very unhappy that systemd +doesn't like their that. It seems that there is no actual problem with allowing +dots in the username. In particular chown declares ":" as the official +separator, and internally in systemd we never rely on "." as the seperator +between user and group (nor do we call chown directly). Using dots in the name +is probably not a very good idea, but we don't need to care. Debian tools +(adduser) do not allow users with dots to be created. + +This patch allows *existing* names with dots to be used in User, Group, +SupplementaryGroups, SocketUser, SocketGroup fields, both in unit files and on +the command line. DynamicUsers and sysusers still follow the strict policy. +user@.service and tmpfiles already allowed arbitrary user names, and this +remains unchanged. + +Fixes #12754. + +(cherry picked from commit ae480f0b09aec815b64579bb1828ea935d8ee236) + +Related: #1848373 +--- + src/core/dbus-execute.c | 12 ++++++------ + src/core/dbus-socket.c | 4 ++-- + src/core/dbus-util.c | 2 +- + src/core/dbus-util.h | 2 +- + src/core/load-fragment-gperf.gperf.m4 | 10 +++++----- + src/core/load-fragment.c | 8 ++++---- + src/core/load-fragment.h | 4 ++-- + 7 files changed, 21 insertions(+), 21 deletions(-) + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 0fe4c14e48..e004fb55c9 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1113,10 +1113,10 @@ int bus_exec_context_set_transient_property( + flags |= UNIT_PRIVATE; + + if (streq(name, "User")) +- return bus_set_transient_user(u, name, &c->user, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &c->user, message, flags, error); + + if (streq(name, "Group")) +- return bus_set_transient_user(u, name, &c->group, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &c->group, message, flags, error); + + if (streq(name, "TTYPath")) + return bus_set_transient_path(u, name, &c->tty_path, message, flags, error); +@@ -1297,10 +1297,10 @@ int bus_exec_context_set_transient_property( + if (r < 0) + return r; + +- STRV_FOREACH(p, l) { +- if (!isempty(*p) && !valid_user_group_name_or_id(*p)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid supplementary group names"); +- } ++ STRV_FOREACH(p, l) ++ if (!isempty(*p) && !valid_user_group_name_or_id_compat(*p)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, ++ "Invalid supplementary group names"); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (strv_isempty(l)) { +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index bb77539030..8fdbc05409 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -281,10 +281,10 @@ static int bus_socket_set_transient_property( + return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error); + + if (streq(name, "SocketUser")) +- return bus_set_transient_user(u, name, &s->user, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &s->user, message, flags, error); + + if (streq(name, "SocketGroup")) +- return bus_set_transient_user(u, name, &s->group, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &s->group, message, flags, error); + + if (streq(name, "BindIPv6Only")) + return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error); +diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c +index f4fbb72cb9..7862beaacb 100644 +--- a/src/core/dbus-util.c ++++ b/src/core/dbus-util.c +@@ -30,7 +30,7 @@ int bus_property_get_triggered_unit( + + BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o"); + BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32); +-BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user, valid_user_group_name_or_id); ++BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_compat, valid_user_group_name_or_id_compat); + BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute); + + int bus_set_transient_string( +diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h +index 12b055e4ac..a3316c6701 100644 +--- a/src/core/dbus-util.h ++++ b/src/core/dbus-util.h +@@ -235,7 +235,7 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i + + int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +-int bus_set_transient_user(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); ++int bus_set_transient_user_compat(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 24ee5ae6fe..156a4d0a6d 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -25,9 +25,9 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', + `$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) + $1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory) + $1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image) +-$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user) +-$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group) +-$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups) ++$1.User, config_parse_user_group_compat, 0, offsetof($1, exec_context.user) ++$1.Group, config_parse_user_group_compat, 0, offsetof($1, exec_context.group) ++$1.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof($1, exec_context.supplementary_groups) + $1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) + $1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) + $1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) +@@ -354,8 +354,8 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC + Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) + Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) + Socket.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Socket, timeout_usec) +-Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user) +-Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group) ++Socket.SocketUser, config_parse_user_group_compat, 0, offsetof(Socket, user) ++Socket.SocketGroup, config_parse_user_group_compat, 0, offsetof(Socket, group) + Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) + Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) + Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 740401a582..ba81d94504 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1899,7 +1899,7 @@ int config_parse_sec_fix_0( + return 0; + } + +-int config_parse_user_group( ++int config_parse_user_group_compat( + const char *unit, + const char *filename, + unsigned line, +@@ -1932,7 +1932,7 @@ int config_parse_user_group( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id(k)) { ++ if (!valid_user_group_name_or_id_compat(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +@@ -1940,7 +1940,7 @@ int config_parse_user_group( + return free_and_replace(*user, k); + } + +-int config_parse_user_group_strv( ++int config_parse_user_group_strv_compat( + const char *unit, + const char *filename, + unsigned line, +@@ -1986,7 +1986,7 @@ int config_parse_user_group_strv( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id(k)) { ++ if (!valid_user_group_name_or_id_compat(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index 65a94d53cc..f9d34d484d 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -96,8 +96,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_utmp_mode); + CONFIG_PARSER_PROTOTYPE(config_parse_working_directory); + CONFIG_PARSER_PROTOTYPE(config_parse_fdname); + CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0); +-CONFIG_PARSER_PROTOTYPE(config_parse_user_group); +-CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv); ++CONFIG_PARSER_PROTOTYPE(config_parse_user_group_compat); ++CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv_compat); + CONFIG_PARSER_PROTOTYPE(config_parse_restrict_namespaces); + CONFIG_PARSER_PROTOTYPE(config_parse_bind_paths); + CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode); diff --git a/SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch b/SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch new file mode 100644 index 0000000..b982f8c --- /dev/null +++ b/SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch @@ -0,0 +1,38 @@ +From 7569168bea3d7e11cd3afe6167fcf4a3ac65a1a6 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 30 Mar 2020 21:46:01 +0200 +Subject: [PATCH] user-util: switch order of checks in + valid_user_group_name_or_id_full() + +When we are supposed to accept numeric UIDs formatted as string, then +let's check that first, before passing things on to +valid_user_group_name_full(), since that might log about, and not the +other way round. + +See: #15201 +Follow-up for: 93c23c9297e48e594785e0bb9c51504aae5fbe3e + +(cherry picked from commit a85daa0dfb3eb03be9845760e90e54b9af8fb00e) + +Related: #1848373 +--- + src/basic/user-util.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 7dd2bb2c84..68a924770b 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -656,10 +656,10 @@ bool valid_user_group_name_or_id_full(const char *u, bool strict) { + if (isempty(u)) + return false; + +- if (valid_user_group_name_full(u, strict)) ++ if (parse_uid(u, NULL) >= 0) + return true; + +- return parse_uid(u, NULL) >= 0; ++ return valid_user_group_name_full(u, strict); + } + + bool valid_gecos(const char *d) { diff --git a/SOURCES/0474-user-util-rework-how-we-validate-user-names.patch b/SOURCES/0474-user-util-rework-how-we-validate-user-names.patch new file mode 100644 index 0000000..9607f81 --- /dev/null +++ b/SOURCES/0474-user-util-rework-how-we-validate-user-names.patch @@ -0,0 +1,803 @@ +From 33b851f0c30e47fe71a293e2c990ef26573efe86 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 4 Apr 2020 12:23:02 +0200 +Subject: [PATCH] user-util: rework how we validate user names +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reworks the user validation infrastructure. There are now two +modes. In regular mode we are strict and test against a strict set of +valid chars. And in "relaxed" mode we just filter out some really +obvious, dangerous stuff. i.e. strict is whitelisting what is OK, but +"relaxed" is blacklisting what is really not OK. + +The idea is that we use strict mode whenver we allocate a new user +(i.e. in sysusers.d or homed), while "relaxed" mode is when we process +users registered elsewhere, (i.e. userdb, logind, …) + +The requirements on user name validity vary wildly. SSSD thinks its fine +to embedd "@" for example, while the suggested NAME_REGEX field on +Debian does not even allow uppercase chars… + +This effectively liberaralizes a lot what we expect from usernames. + +The code that warns about questionnable user names is now optional and +only used at places such as unit file parsing, so that it doesn't show +up on every userdb query, but only when processing configuration files +that know better. + +Fixes: #15149 #15090 +(cherry picked from commit 7a8867abfab10e5bbca10590ec2aa40c5b27d8fb) + +Resolves: #1848373 +--- + src/basic/user-util.c | 185 +++++++++++++---------- + src/basic/user-util.h | 21 +-- + src/core/dbus-execute.c | 6 +- + src/core/dbus-manager.c | 2 +- + src/core/dbus-socket.c | 4 +- + src/core/dbus-util.c | 7 +- + src/core/dbus-util.h | 2 +- + src/core/dynamic-user.c | 2 +- + src/core/load-fragment.c | 4 +- + src/core/unit.c | 2 +- + src/nss-systemd/nss-systemd.c | 6 +- + src/systemd/sd-messages.h | 3 + + src/sysusers/sysusers.c | 4 +- + src/test/test-user-util.c | 271 ++++++++++++++++++---------------- + 14 files changed, 287 insertions(+), 232 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 68a924770b..cd870c4361 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -14,6 +14,8 @@ + #include + #include + ++#include "sd-messages.h" ++ + #include "alloc-util.h" + #include "fd-util.h" + #include "fileio.h" +@@ -576,92 +578,125 @@ int take_etc_passwd_lock(const char *root) { + return fd; + } + +-bool valid_user_group_name_full(const char *u, bool strict) { ++bool valid_user_group_name(const char *u, ValidUserFlags flags) { + const char *i; +- long sz; +- bool warned = false; + +- /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, +- * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules: +- * +- * - We require that names fit into the appropriate utmp field +- * - We don't allow empty user names +- * - No dots in the first character ++ /* Checks if the specified name is a valid user/group name. There are two flavours of this call: ++ * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept ++ * pretty much everything except the really worst offending names. + * +- * If strict==true, additionally: +- * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) +- * - We don't allow a digit as the first character +- * +- * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. +- */ ++ * Whenever we synthesize users ourselves we should use the strict mode. But when we process users ++ * created by other stuff, let's be more liberal. */ + +- if (isempty(u)) ++ if (isempty(u)) /* An empty user name is never valid */ + return false; + +- if (!(u[0] >= 'a' && u[0] <= 'z') && +- !(u[0] >= 'A' && u[0] <= 'Z') && +- !(u[0] >= '0' && u[0] <= '9' && !strict) && +- u[0] != '_') +- return false; +- +- bool only_digits_seen = u[0] >= '0' && u[0] <= '9'; +- +- if (only_digits_seen) { +- log_warning("User or group name \"%s\" starts with a digit, accepting for compatibility.", u); +- warned = true; +- } +- +- for (i = u+1; *i; i++) { +- if (((*i >= 'a' && *i <= 'z') || +- (*i >= 'A' && *i <= 'Z') || +- (*i >= '0' && *i <= '9') || +- IN_SET(*i, '_', '-'))) { +- if (!(*i >= '0' && *i <= '9')) +- only_digits_seen = false; +- continue; +- } +- +- if (*i == '.' && !strict) { +- if (!warned) { +- log_warning("Bad user or group name \"%s\", accepting for compatibility.", u); +- warned = true; +- } +- +- continue; +- } +- +- return false; ++ if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the ++ * flag for it is set */ ++ return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC); ++ ++ if (FLAGS_SET(flags, VALID_USER_RELAX)) { ++ ++ /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is ++ * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which ++ * is bound to cause problems for example when used with an MTA), hence only filter the most ++ * obvious cases, or where things would result in an invalid entry if such a user name would ++ * show up in /etc/passwd (or equivalent getent output). ++ * ++ * Note that we stepped far out of POSIX territory here. It's not our fault though, but ++ * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step ++ * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't ++ * have...) */ ++ ++ if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed ++ * at front and back (accept in the middle, since ++ * that's apparently a thing on Windows). Note ++ * that this also blocks usernames consisting of ++ * whitespace only. */ ++ return false; ++ ++ if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */ ++ return false; ++ ++ if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the ++ * record separator in /etc/passwd), so we can't allow that. */ ++ return false; ++ ++ if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow ++ * that. Slashes are special to file systems paths and user names ++ * typically show up in the file system as home directories, hence ++ * don't allow slashes. */ ++ return false; ++ ++ if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused ++ * with with UIDs (note that this test is more broad than ++ * the parse_uid() test above, as it will cover more than ++ * the 32bit range, and it will detect 65535 (which is in ++ * invalid UID, even though in the unsigned 32 bit range) */ ++ return false; ++ ++ if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric ++ * strings either. After all some people ++ * write 65535 as -1 (even though that's ++ * not even true on 32bit uid_t ++ * anyway) */ ++ return false; ++ ++ if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are ++ * special in that context, don't allow that. */ ++ return false; ++ ++ /* Compare with strict result and warn if result doesn't match */ ++ if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0)) ++ log_struct(LOG_NOTICE, ++ "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules.", u, ++ "USER_GROUP_NAME=%s", u, ++ "MESSAGE_ID=" SD_MESSAGE_UNSAFE_USER_NAME_STR); ++ ++ /* Note that we make no restrictions on the length in relaxed mode! */ ++ } else { ++ long sz; ++ size_t l; ++ ++ /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here ++ * however. Specifically we deviate from POSIX rules: ++ * ++ * - We don't allow empty user names (see above) ++ * - We require that names fit into the appropriate utmp field ++ * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) ++ * - We don't allow dashes or digit as the first character ++ * ++ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. ++ */ ++ ++ if (!(u[0] >= 'a' && u[0] <= 'z') && ++ !(u[0] >= 'A' && u[0] <= 'Z') && ++ u[0] != '_') ++ return false; ++ ++ for (i = u+1; *i; i++) ++ if (!(*i >= 'a' && *i <= 'z') && ++ !(*i >= 'A' && *i <= 'Z') && ++ !(*i >= '0' && *i <= '9') && ++ !IN_SET(*i, '_', '-')) ++ return false; ++ ++ l = i - u; ++ ++ sz = sysconf(_SC_LOGIN_NAME_MAX); ++ assert_se(sz > 0); ++ ++ if (l > (size_t) sz) ++ return false; ++ if (l > FILENAME_MAX) ++ return false; ++ if (l > UT_NAMESIZE - 1) ++ return false; + } + +- if (only_digits_seen) +- return false; +- +- sz = sysconf(_SC_LOGIN_NAME_MAX); +- assert_se(sz > 0); +- +- if ((size_t) (i-u) > (size_t) sz) +- return false; +- +- if ((size_t) (i-u) > UT_NAMESIZE - 1) +- return false; +- + return true; + } + +-bool valid_user_group_name_or_id_full(const char *u, bool strict) { +- +- /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the +- * right range, and not the invalid user ids. */ +- +- if (isempty(u)) +- return false; +- +- if (parse_uid(u, NULL) >= 0) +- return true; +- +- return valid_user_group_name_full(u, strict); +-} +- + bool valid_gecos(const char *d) { + + if (!d) +diff --git a/src/basic/user-util.h b/src/basic/user-util.h +index 5ad0b2a2f9..939bded40d 100644 +--- a/src/basic/user-util.h ++++ b/src/basic/user-util.h +@@ -78,20 +78,13 @@ static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; + } + +-bool valid_user_group_name_full(const char *u, bool strict); +-bool valid_user_group_name_or_id_full(const char *u, bool strict); +-static inline bool valid_user_group_name(const char *u) { +- return valid_user_group_name_full(u, true); +-} +-static inline bool valid_user_group_name_or_id(const char *u) { +- return valid_user_group_name_or_id_full(u, true); +-} +-static inline bool valid_user_group_name_compat(const char *u) { +- return valid_user_group_name_full(u, false); +-} +-static inline bool valid_user_group_name_or_id_compat(const char *u) { +- return valid_user_group_name_or_id_full(u, false); +-} ++typedef enum ValidUserFlags { ++ VALID_USER_RELAX = 1 << 0, ++ VALID_USER_WARN = 1 << 1, ++ VALID_USER_ALLOW_NUMERIC = 1 << 2, ++} ValidUserFlags; ++ ++bool valid_user_group_name(const char *u, ValidUserFlags flags); + bool valid_gecos(const char *d); + bool valid_home(const char *p); + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index e004fb55c9..8348663000 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1113,10 +1113,10 @@ int bus_exec_context_set_transient_property( + flags |= UNIT_PRIVATE; + + if (streq(name, "User")) +- return bus_set_transient_user_compat(u, name, &c->user, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &c->user, message, flags, error); + + if (streq(name, "Group")) +- return bus_set_transient_user_compat(u, name, &c->group, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &c->group, message, flags, error); + + if (streq(name, "TTYPath")) + return bus_set_transient_path(u, name, &c->tty_path, message, flags, error); +@@ -1298,7 +1298,7 @@ int bus_exec_context_set_transient_property( + return r; + + STRV_FOREACH(p, l) +- if (!isempty(*p) && !valid_user_group_name_or_id_compat(*p)) ++ if (!isempty(*p) && !valid_user_group_name(*p, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid supplementary group names"); + +diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c +index 0a1d3df42f..7488f22116 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -1762,7 +1762,7 @@ static int method_lookup_dynamic_user_by_name(sd_bus_message *message, void *use + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance."); +- if (!valid_user_group_name(name)) ++ if (!valid_user_group_name(name, VALID_USER_RELAX)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name invalid: %s", name); + + r = dynamic_user_lookup_name(m, name, &uid); +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index 8fdbc05409..fa6bbe2c6f 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -281,10 +281,10 @@ static int bus_socket_set_transient_property( + return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error); + + if (streq(name, "SocketUser")) +- return bus_set_transient_user_compat(u, name, &s->user, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &s->user, message, flags, error); + + if (streq(name, "SocketGroup")) +- return bus_set_transient_user_compat(u, name, &s->group, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &s->group, message, flags, error); + + if (streq(name, "BindIPv6Only")) + return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error); +diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c +index 7862beaacb..951450e53d 100644 +--- a/src/core/dbus-util.c ++++ b/src/core/dbus-util.c +@@ -30,7 +30,12 @@ int bus_property_get_triggered_unit( + + BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o"); + BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32); +-BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_compat, valid_user_group_name_or_id_compat); ++ ++static inline bool valid_user_group_name_or_id_relaxed(const char *u) { ++ return valid_user_group_name(u, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX); ++} ++ ++BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_relaxed, valid_user_group_name_or_id_relaxed); + BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute); + + int bus_set_transient_string( +diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h +index a3316c6701..713b464dd9 100644 +--- a/src/core/dbus-util.h ++++ b/src/core/dbus-util.h +@@ -235,7 +235,7 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i + + int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +-int bus_set_transient_user_compat(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); ++int bus_set_transient_user_relaxed(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c +index 021fd93a76..548b3cc9df 100644 +--- a/src/core/dynamic-user.c ++++ b/src/core/dynamic-user.c +@@ -108,7 +108,7 @@ static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) + return 0; + } + +- if (!valid_user_group_name_or_id(name)) ++ if (!valid_user_group_name(name, VALID_USER_ALLOW_NUMERIC)) + return -EINVAL; + + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index ba81d94504..e0d7b8f7f8 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1932,7 +1932,7 @@ int config_parse_user_group_compat( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id_compat(k)) { ++ if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +@@ -1986,7 +1986,7 @@ int config_parse_user_group_strv_compat( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id_compat(k)) { ++ if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +diff --git a/src/core/unit.c b/src/core/unit.c +index ffbf3cfd48..cd3e7c806d 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -4088,7 +4088,7 @@ static int user_from_unit_name(Unit *u, char **ret) { + if (r < 0) + return r; + +- if (valid_user_group_name(n)) { ++ if (valid_user_group_name(n, 0)) { + *ret = TAKE_PTR(n); + return 0; + } +diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c +index f8db27ae27..615c710257 100644 +--- a/src/nss-systemd/nss-systemd.c ++++ b/src/nss-systemd/nss-systemd.c +@@ -123,7 +123,7 @@ static int direct_lookup_uid(uid_t uid, char **ret) { + r = readlink_malloc(path, &s); + if (r < 0) + return r; +- if (!valid_user_group_name(s)) { /* extra safety check */ ++ if (!valid_user_group_name(s, VALID_USER_RELAX)) { /* extra safety check */ + free(s); + return -EINVAL; + } +@@ -153,7 +153,7 @@ enum nss_status _nss_systemd_getpwnam_r( + + /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't + * generate EINVAL here, because it isn't really out business to complain about invalid user names. */ +- if (!valid_user_group_name(name)) ++ if (!valid_user_group_name(name, VALID_USER_RELAX)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ +@@ -356,7 +356,7 @@ enum nss_status _nss_systemd_getgrnam_r( + assert(name); + assert(gr); + +- if (!valid_user_group_name(name)) ++ if (!valid_user_group_name(name, VALID_USER_RELAX)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize records for root and nobody, in case they are missing form /etc/group */ +diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h +index bdd4fd3974..847b698ba4 100644 +--- a/src/systemd/sd-messages.h ++++ b/src/systemd/sd-messages.h +@@ -152,6 +152,9 @@ _SD_BEGIN_DECLARATIONS; + #define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + #define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + ++#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) ++#define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) ++ + _SD_END_DECLARATIONS; + + #endif +diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c +index 33959d3c11..a374ebaaf4 100644 +--- a/src/sysusers/sysusers.c ++++ b/src/sysusers/sysusers.c +@@ -1413,7 +1413,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { + return r; + } + +- if (!valid_user_group_name(resolved_name)) { ++ if (!valid_user_group_name(resolved_name, 0)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); + return -EINVAL; + } +@@ -1524,7 +1524,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { + return -EINVAL; + } + +- if (!valid_user_group_name(resolved_id)) { ++ if (!valid_user_group_name(resolved_id, 0)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); + return -EINVAL; + } +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 56079f1486..31ac018da9 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -131,144 +131,163 @@ static void test_uid_ptr(void) { + assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); + } + +-static void test_valid_user_group_name_compat(void) { ++static void test_valid_user_group_name_relaxed(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name_compat(NULL)); +- assert_se(!valid_user_group_name_compat("")); +- assert_se(!valid_user_group_name_compat("1")); +- assert_se(!valid_user_group_name_compat("65535")); +- assert_se(!valid_user_group_name_compat("-1")); +- assert_se(!valid_user_group_name_compat("-kkk")); +- assert_se(!valid_user_group_name_compat("rööt")); +- assert_se(!valid_user_group_name_compat(".")); +- assert_se(!valid_user_group_name_compat(".eff")); +- assert_se(!valid_user_group_name_compat("foo\nbar")); +- assert_se(!valid_user_group_name_compat("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); +- assert_se(!valid_user_group_name_compat(".")); +- assert_se(!valid_user_group_name_compat(".1")); +- assert_se(!valid_user_group_name_compat(".65535")); +- assert_se(!valid_user_group_name_compat(".-1")); +- assert_se(!valid_user_group_name_compat(".-kkk")); +- assert_se(!valid_user_group_name_compat(".rööt")); +- assert_se(!valid_user_group_name_or_id_compat(".aaa:bbb")); +- +- assert_se(valid_user_group_name_compat("root")); +- assert_se(valid_user_group_name_compat("lennart")); +- assert_se(valid_user_group_name_compat("LENNART")); +- assert_se(valid_user_group_name_compat("_kkk")); +- assert_se(valid_user_group_name_compat("kkk-")); +- assert_se(valid_user_group_name_compat("kk-k")); +- assert_se(valid_user_group_name_compat("eff.eff")); +- assert_se(valid_user_group_name_compat("eff.")); +- +- assert_se(valid_user_group_name_compat("some5")); +- assert_se(valid_user_group_name_compat("5some")); +- assert_se(valid_user_group_name_compat("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("1", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("65535", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("-1", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("foo\nbar", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("..", VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("root", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("lennart", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("LENNART", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("_kkk", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kkk-", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kk-k", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.eff", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("-kkk", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("rööt", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".eff", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".1", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".65535", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".-1", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".-kkk", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".rööt", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("...", VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("some5", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("5some", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("Dāvis", VALID_USER_RELAX)); + } + + static void test_valid_user_group_name(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name(NULL)); +- assert_se(!valid_user_group_name("")); +- assert_se(!valid_user_group_name("1")); +- assert_se(!valid_user_group_name("65535")); +- assert_se(!valid_user_group_name("-1")); +- assert_se(!valid_user_group_name("-kkk")); +- assert_se(!valid_user_group_name("rööt")); +- assert_se(!valid_user_group_name(".")); +- assert_se(!valid_user_group_name(".eff")); +- assert_se(!valid_user_group_name("foo\nbar")); +- assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id("aaa:bbb")); +- assert_se(!valid_user_group_name(".")); +- assert_se(!valid_user_group_name(".1")); +- assert_se(!valid_user_group_name(".65535")); +- assert_se(!valid_user_group_name(".-1")); +- assert_se(!valid_user_group_name(".-kkk")); +- assert_se(!valid_user_group_name(".rööt")); +- assert_se(!valid_user_group_name_or_id(".aaa:bbb")); +- +- assert_se(valid_user_group_name("root")); +- assert_se(valid_user_group_name("lennart")); +- assert_se(valid_user_group_name("LENNART")); +- assert_se(valid_user_group_name("_kkk")); +- assert_se(valid_user_group_name("kkk-")); +- assert_se(valid_user_group_name("kk-k")); +- assert_se(!valid_user_group_name("eff.eff")); +- assert_se(!valid_user_group_name("eff.")); +- +- assert_se(valid_user_group_name("some5")); +- assert_se(!valid_user_group_name("5some")); +- assert_se(valid_user_group_name("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, 0)); ++ assert_se(!valid_user_group_name("", 0)); ++ assert_se(!valid_user_group_name("1", 0)); ++ assert_se(!valid_user_group_name("65535", 0)); ++ assert_se(!valid_user_group_name("-1", 0)); ++ assert_se(!valid_user_group_name("-kkk", 0)); ++ assert_se(!valid_user_group_name("rööt", 0)); ++ assert_se(!valid_user_group_name(".", 0)); ++ assert_se(!valid_user_group_name(".eff", 0)); ++ assert_se(!valid_user_group_name("foo\nbar", 0)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", 0)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".", 0)); ++ assert_se(!valid_user_group_name("..", 0)); ++ assert_se(!valid_user_group_name("...", 0)); ++ assert_se(!valid_user_group_name(".1", 0)); ++ assert_se(!valid_user_group_name(".65535", 0)); ++ assert_se(!valid_user_group_name(".-1", 0)); ++ assert_se(!valid_user_group_name(".-kkk", 0)); ++ assert_se(!valid_user_group_name(".rööt", 0)); ++ assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(valid_user_group_name("root", 0)); ++ assert_se(valid_user_group_name("lennart", 0)); ++ assert_se(valid_user_group_name("LENNART", 0)); ++ assert_se(valid_user_group_name("_kkk", 0)); ++ assert_se(valid_user_group_name("kkk-", 0)); ++ assert_se(valid_user_group_name("kk-k", 0)); ++ assert_se(!valid_user_group_name("eff.eff", 0)); ++ assert_se(!valid_user_group_name("eff.", 0)); ++ ++ assert_se(valid_user_group_name("some5", 0)); ++ assert_se(!valid_user_group_name("5some", 0)); ++ assert_se(valid_user_group_name("INNER5NUMBER", 0)); ++ ++ assert_se(!valid_user_group_name("piff.paff@ad.domain.example", 0)); ++ assert_se(!valid_user_group_name("Dāvis", 0)); + } + +-static void test_valid_user_group_name_or_id_compat(void) { ++static void test_valid_user_group_name_or_numeric_relaxed(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name_or_id_compat(NULL)); +- assert_se(!valid_user_group_name_or_id_compat("")); +- assert_se(valid_user_group_name_or_id_compat("0")); +- assert_se(valid_user_group_name_or_id_compat("1")); +- assert_se(valid_user_group_name_or_id_compat("65534")); +- assert_se(!valid_user_group_name_or_id_compat("65535")); +- assert_se(valid_user_group_name_or_id_compat("65536")); +- assert_se(!valid_user_group_name_or_id_compat("-1")); +- assert_se(!valid_user_group_name_or_id_compat("-kkk")); +- assert_se(!valid_user_group_name_or_id_compat("rööt")); +- assert_se(!valid_user_group_name_or_id_compat(".")); +- assert_se(!valid_user_group_name_or_id_compat(".eff")); +- assert_se(valid_user_group_name_or_id_compat("eff.eff")); +- assert_se(valid_user_group_name_or_id_compat("eff.")); +- assert_se(!valid_user_group_name_or_id_compat("foo\nbar")); +- assert_se(!valid_user_group_name_or_id_compat("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); +- +- assert_se(valid_user_group_name_or_id_compat("root")); +- assert_se(valid_user_group_name_or_id_compat("lennart")); +- assert_se(valid_user_group_name_or_id_compat("LENNART")); +- assert_se(valid_user_group_name_or_id_compat("_kkk")); +- assert_se(valid_user_group_name_or_id_compat("kkk-")); +- assert_se(valid_user_group_name_or_id_compat("kk-k")); +- +- assert_se(valid_user_group_name_or_id_compat("some5")); +- assert_se(valid_user_group_name_or_id_compat("5some")); +- assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); + } + +-static void test_valid_user_group_name_or_id(void) { ++static void test_valid_user_group_name_or_numeric(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name_or_id(NULL)); +- assert_se(!valid_user_group_name_or_id("")); +- assert_se(valid_user_group_name_or_id("0")); +- assert_se(valid_user_group_name_or_id("1")); +- assert_se(valid_user_group_name_or_id("65534")); +- assert_se(!valid_user_group_name_or_id("65535")); +- assert_se(valid_user_group_name_or_id("65536")); +- assert_se(!valid_user_group_name_or_id("-1")); +- assert_se(!valid_user_group_name_or_id("-kkk")); +- assert_se(!valid_user_group_name_or_id("rööt")); +- assert_se(!valid_user_group_name_or_id(".")); +- assert_se(!valid_user_group_name_or_id(".eff")); +- assert_se(!valid_user_group_name_or_id("eff.eff")); +- assert_se(!valid_user_group_name_or_id("eff.")); +- assert_se(!valid_user_group_name_or_id("foo\nbar")); +- assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id("aaa:bbb")); +- +- assert_se(valid_user_group_name_or_id("root")); +- assert_se(valid_user_group_name_or_id("lennart")); +- assert_se(valid_user_group_name_or_id("LENNART")); +- assert_se(valid_user_group_name_or_id("_kkk")); +- assert_se(valid_user_group_name_or_id("kkk-")); +- assert_se(valid_user_group_name_or_id("kk-k")); +- +- assert_se(valid_user_group_name_or_id("some5")); +- assert_se(!valid_user_group_name_or_id("5some")); +- assert_se(valid_user_group_name_or_id("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(!valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC)); + } + + static void test_valid_gecos(void) { +@@ -367,10 +386,10 @@ int main(int argc, char*argv[]) { + test_parse_uid(); + test_uid_ptr(); + +- test_valid_user_group_name_compat(); ++ test_valid_user_group_name_relaxed(); + test_valid_user_group_name(); +- test_valid_user_group_name_or_id_compat(); +- test_valid_user_group_name_or_id(); ++ test_valid_user_group_name_or_numeric_relaxed(); ++ test_valid_user_group_name_or_numeric(); + test_valid_gecos(); + test_valid_home(); + diff --git a/SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch b/SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch new file mode 100644 index 0000000..0fed0ab --- /dev/null +++ b/SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch @@ -0,0 +1,35 @@ +From 11a9ea82827d7b57dbce307b77ef8233a4cc028a Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Thu, 28 Aug 2014 15:12:10 +0200 +Subject: [PATCH] man: mention System Administrator's Guide in systemctl + manpage + +(cherry picked from commit d4582346f47064de24470b5f92e418966004925f) + +Resolves: #1623116 +--- + man/systemctl.xml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index fa08ab6c0a..56f94d084c 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -2000,6 +2000,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + ++ ++ Examples ++ ++ For examples how to use systemctl in comparsion ++ with old service and chkconfig command please see: ++ ++ Managing System Services ++ ++ ++ ++ + + See Also + diff --git a/SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch b/SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch new file mode 100644 index 0000000..d218efb --- /dev/null +++ b/SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch @@ -0,0 +1,257 @@ +From 08ac9f7f55c138678c6415139e7510a05a75b81d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Wed, 14 Oct 2020 16:57:44 +0200 +Subject: [PATCH] udev: introduce udev net_id "naming schemes" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this we can stabilize how naming works for network interfaces. A +user can request through a kernel cmdline option or an env var which +scheme to follow. The idea is that installers use this to set into stone +(a very soft stone though) the scheme used during installation so that +interface naming doesn't change afterwards anymore. + +Why use env vars and kernel cmdline options, and not a config file of +its own? + +Well, first of all there's no obvious existing one to use. But more +importantly: I have the feeling that this logic is kind of an incomplete +hack, and I simply don't want to do advertise this as a perfectly +working solution. So far we used env vars for the non-so-official +options and proper config files for the official stuff. Given how +incomplete this logic is (i.e. the big variable for naming remains the +kernel, which might expose sysfs attributes in newer versions that we +check for and didn't exist in older versions — and other problems like +this), I am simply not confident in giving this first-class exposure in +a primary configuration file. + +Fixes: #10448 + +(cherry-picked from commit f7e81fd96fdfe0ac6dcdb72de43f7cb4720e363a) + +Related: #1827462 + +[msekleta: note that we are introducing our own naming schemes based on +RHEL-8 minor versions. Also we are not backporting all naming scheme +features that appeared in the original commit. We are backporting only +features relevant for v239 while original commit also converted +changes introduced in v240 into naming scheme flags.] +--- + doc/ENVIRONMENT.md | 9 +++ + man/kernel-command-line.xml | 1 + + man/systemd-udevd.service.xml | 16 +++++ + src/udev/udev-builtin-net_id.c | 106 ++++++++++++++++++++++++++++++++- + 4 files changed, 130 insertions(+), 2 deletions(-) + +diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md +index 39a36a52cc..1a4aa01ef4 100644 +--- a/doc/ENVIRONMENT.md ++++ b/doc/ENVIRONMENT.md +@@ -76,6 +76,15 @@ systemd-logind: + hibernation is available even if the swap devices do not provide enough room + for it. + ++* `$NET_NAMING_SCHEME=` – if set, takes a network naming scheme (i.e. one of ++ v238, v239, v240 …) as parameter. If specified udev's net_id builtin will ++ follow the specified naming scheme when determining stable network interface ++ names. This may be used to revert to naming schemes of older udev versions, ++ in order to provide more stable naming across updates. This environment ++ variable takes precedence over the kernel command line option ++ `net.naming-scheme=`, except if the value is prefixed with `:` in which case ++ the kernel command line option takes precedence, if it is specified as well. ++ + installed systemd tests: + + * `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if +diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml +index 4d8cb4e50e..b753d0592c 100644 +--- a/man/kernel-command-line.xml ++++ b/man/kernel-command-line.xml +@@ -246,6 +246,7 @@ + udev.event_timeout= + rd.udev.event_timeout= + net.ifnames= ++ net.naming-scheme= + + + Parameters understood by the device event managing +diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml +index 73c77ea690..6449103441 100644 +--- a/man/systemd-udevd.service.xml ++++ b/man/systemd-udevd.service.xml +@@ -170,6 +170,22 @@ + when possible. It is enabled by default; specifying 0 disables it. + + ++ ++ net.naming-scheme= ++ ++ Network interfaces are renamed to give them predictable names when possible (unless ++ net.ifnames=0 is specified, see above). The names are derived from various device metadata ++ fields. Newer versions of systemd-udevd.service take more of these fields into account, ++ improving (and thus possibly changing) the names used for the same devices. With this kernel command line ++ option it is possible to pick a specific version of this algorithm. It expects a naming scheme identifier as ++ argument. Currently the following identifiers are known: v238, v239, ++ v240 which each implement the naming scheme that was the default in the indicated systemd ++ version. Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the ++ naming is generally derived from driver attributes exposed by the kernel. As the kernel is updated, ++ previously missing attributes systemd-udevd.service is checking might appear, which ++ affects older name derivation algorithms, too. ++ ++ + + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index 147e04ab8c..148696183e 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -96,6 +96,7 @@ + #include "fileio.h" + #include "fs-util.h" + #include "parse-util.h" ++#include "proc-cmdline.h" + #include "stdio-util.h" + #include "string-util.h" + #include "udev.h" +@@ -103,6 +104,52 @@ + + #define ONBOARD_INDEX_MAX (16*1024-1) + ++/* So here's the deal: net_id is supposed to be an excercise in providing stable names for network devices. However, we ++ * also want to keep updating the naming scheme used in future versions of net_id. These two goals of course are ++ * contradictory: on one hand we want things to not change and on the other hand we want them to improve. Our way out ++ * of this dilemma is to introduce the "naming scheme" concept: each time we improve the naming logic we define a new ++ * flag for it. Then, we keep a list of schemes, each identified by a name associated with the flags it implements. Via ++ * a kernel command line and environment variable we then allow the user to pick the scheme they want us to follow: ++ * installers could "freeze" the used scheme at the moment of installation this way. ++ * ++ * Developers: each time you tweak the naming logic here, define a new flag below, and condition the tweak with ++ * it. Each time we do a release we'll then add a new scheme entry and include all newly defined flags. ++ * ++ * Note that this is only half a solution to the problem though: not only udev/net_id gets updated all the time, the ++ * kernel gets too. And thus a kernel that previously didn't expose some sysfs attribute we look for might eventually ++ * do, and thus affect our naming scheme too. Thus, enforcing a naming scheme will make interfacing more stable across ++ * OS versions, but not fully stabilize them. */ ++typedef enum NamingSchemeFlags { ++ /* First, the individual features */ ++ NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/ ++ NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */ ++ ++ /* And now the masks that combine the features above */ ++ NAMING_V238 = 0, ++ NAMING_V239 = NAMING_V238|NAMING_SR_IOV_V|NAMING_NPAR_ARI, ++ NAMING_RHEL_8_0 = NAMING_V239, ++ NAMING_RHEL_8_1 = NAMING_V239, ++ NAMING_RHEL_8_2 = NAMING_V239, ++ NAMING_RHEL_8_3 = NAMING_V239, ++ ++ _NAMING_SCHEME_FLAGS_INVALID = -1, ++} NamingSchemeFlags; ++ ++typedef struct NamingScheme { ++ const char *name; ++ NamingSchemeFlags flags; ++} NamingScheme; ++ ++static const NamingScheme naming_schemes[] = { ++ { "v238", NAMING_V238 }, ++ { "v239", NAMING_V239 }, ++ { "rhel-8.0", NAMING_RHEL_8_0 }, ++ { "rhel-8.1", NAMING_RHEL_8_1 }, ++ { "rhel-8.2", NAMING_RHEL_8_2 }, ++ { "rhel-8.3", NAMING_RHEL_8_3 }, ++ /* … add more schemes here, as the logic to name devices is updated … */ ++}; ++ + enum netname_type{ + NET_UNDEF, + NET_PCI, +@@ -138,6 +185,56 @@ struct virtfn_info { + char suffix[IFNAMSIZ]; + }; + ++static const NamingScheme* naming_scheme(void) { ++ static const NamingScheme *cache = NULL; ++ _cleanup_free_ char *buffer = NULL; ++ const char *e, *k; ++ ++ if (cache) ++ return cache; ++ ++ /* Acquire setting from the kernel command line */ ++ (void) proc_cmdline_get_key("net.naming-scheme", 0, &buffer); ++ ++ /* Also acquire it from an env var */ ++ e = getenv("NET_NAMING_SCHEME"); ++ if (e) { ++ if (*e == ':') { ++ /* If prefixed with ':' the kernel cmdline takes precedence */ ++ k = buffer ?: e + 1; ++ } else ++ k = e; /* Otherwise the env var takes precedence */ ++ } else ++ k = buffer; ++ ++ if (k) { ++ size_t i; ++ ++ for (i = 0; i < ELEMENTSOF(naming_schemes); i++) ++ if (streq(naming_schemes[i].name, k)) { ++ cache = naming_schemes + i; ++ break; ++ } ++ ++ if (!cache) ++ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k); ++ } ++ ++ if (cache) ++ log_info("Using interface naming scheme '%s'.", cache->name); ++ else { ++ /* RHEL-only: here we differ from the upstream and if no naming scheme was selected we default to naming from systemd-239 */ ++ cache = &naming_schemes[2]; ++ log_info("Using default interface naming scheme '%s'.", cache->name); ++ } ++ ++ return cache; ++} ++ ++static bool naming_scheme_has(NamingSchemeFlags flags) { ++ return FLAGS_SET(naming_scheme()->flags, flags); ++} ++ + /* skip intermediate virtio devices */ + static struct udev_device *skip_virtio(struct udev_device *dev) { + struct udev_device *parent = dev; +@@ -299,7 +396,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + + if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4) + return -ENOENT; +- if (is_pci_ari_enabled(names->pcidev)) ++ ++ if (naming_scheme_has(NAMING_NPAR_ARI) && ++ is_pci_ari_enabled(names->pcidev)) + /* ARI devices support up to 256 functions on a single device ("slot"), and interpret the + * traditional 5-bit slot and 3-bit function number as a single 8-bit function number, + * where the slot makes up the upper 5 bits. */ +@@ -494,7 +593,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) { + return -ENOENT; + } + +- if (get_virtfn_info(dev, names, &vf_info) >= 0) { ++ if (naming_scheme_has(NAMING_SR_IOV_V) && ++ get_virtfn_info(dev, names, &vf_info) >= 0) { + /* If this is an SR-IOV virtual device, get base name using physical device and add virtfn suffix. */ + vf_names.pcidev = vf_info.physfn_pcidev; + dev_pci_onboard(dev, &vf_names); +@@ -741,6 +841,8 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool + prefix = "ww"; + } + ++ udev_builtin_add_property(dev, test, "ID_NET_NAMING_SCHEME", naming_scheme()->name); ++ + err = names_mac(dev, &names); + if (err >= 0 && names.mac_valid) { + char str[IFNAMSIZ]; diff --git a/SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch b/SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch new file mode 100644 index 0000000..0c9f850 --- /dev/null +++ b/SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch @@ -0,0 +1,188 @@ +From 8c263758fe196624005f19bd6f46d63e3841c5be Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 11 Dec 2018 23:28:29 +0100 +Subject: [PATCH] meson: make net.naming-scheme= default configurable + +This is useful for distributions, where the stability of interface names should +be preseved after an upgrade of systemd. So when some specific release of the +distro is made available, systemd defaults to the latest & greatest naming +scheme, and subsequent updates set the same default. This default may still +be overriden through the kernel and env var options. + +A special value "latest" is also allowed. Without a specific name, it is harder +to verride from meson. In case of 'combo' options, meson reads the default +during the initial configuration, and "remembers" this choice. When systemd is +updated, old build/ directories could keep the old default, which would be +annoying. Hence, "latest" is introduced to make it explicit, yet follow the +upstream. This is actually useful for the user too, because it may be used +as an override, without having to actually specify a version. + +(cherry picked from commit 06da5c63dd697ea4087e76c6d809b60b5780b87c) + +Related: #1827462 + +[msekleta: note that our default is not latest but rhel-8.0] +--- + doc/ENVIRONMENT.md | 15 +++++++------- + man/systemd-udevd.service.xml | 24 ++++++++++++--------- + meson.build | 4 ++++ + meson_options.txt | 3 +++ + src/udev/udev-builtin-net_id.c | 38 ++++++++++++++++++++-------------- + 5 files changed, 51 insertions(+), 33 deletions(-) + +diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md +index 1a4aa01ef4..0e763b6302 100644 +--- a/doc/ENVIRONMENT.md ++++ b/doc/ENVIRONMENT.md +@@ -77,13 +77,14 @@ systemd-logind: + for it. + + * `$NET_NAMING_SCHEME=` – if set, takes a network naming scheme (i.e. one of +- v238, v239, v240 …) as parameter. If specified udev's net_id builtin will +- follow the specified naming scheme when determining stable network interface +- names. This may be used to revert to naming schemes of older udev versions, +- in order to provide more stable naming across updates. This environment +- variable takes precedence over the kernel command line option +- `net.naming-scheme=`, except if the value is prefixed with `:` in which case +- the kernel command line option takes precedence, if it is specified as well. ++ "rhel-8.0", "rhel-8.1", "rhel-8.2"…, or the special value "latest") as ++ parameter. If specified udev's net_id builtin will follow the specified ++ naming scheme when determining stable network interface names. This may be ++ used to revert to naming schemes of older udev versions, in order to provide ++ more stable naming across updates. This environment variable takes precedence ++ over the kernel command line option `net.naming-scheme=`, except if the value ++ is prefixed with `:` in which case the kernel command line option takes ++ precedence, if it is specified as well. + + installed systemd tests: + +diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml +index 6449103441..b738591c93 100644 +--- a/man/systemd-udevd.service.xml ++++ b/man/systemd-udevd.service.xml +@@ -174,16 +174,20 @@ + net.naming-scheme= + + Network interfaces are renamed to give them predictable names when possible (unless +- net.ifnames=0 is specified, see above). The names are derived from various device metadata +- fields. Newer versions of systemd-udevd.service take more of these fields into account, +- improving (and thus possibly changing) the names used for the same devices. With this kernel command line +- option it is possible to pick a specific version of this algorithm. It expects a naming scheme identifier as +- argument. Currently the following identifiers are known: v238, v239, +- v240 which each implement the naming scheme that was the default in the indicated systemd +- version. Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the +- naming is generally derived from driver attributes exposed by the kernel. As the kernel is updated, +- previously missing attributes systemd-udevd.service is checking might appear, which +- affects older name derivation algorithms, too. ++ net.ifnames=0 is specified, see above). The names are derived from various ++ device metadata fields. Newer versions of systemd-udevd.service take more of ++ these fields into account, improving (and thus possibly changing) the names used for the same ++ devices. With this kernel command line option it is possible to pick a specific version of this ++ algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers ++ are known: rhel-8.0, rhel-8.1, rhel-8.2, ++ rhel-8.3 which each implement the naming scheme that was the default in the ++ indicated Red Hat Enterprise Linux minor version. In addition, latest may be ++ used to designate the latest scheme known (to this particular version of ++ systemd-udevd.service). ++ Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: ++ the naming is generally derived from driver attributes exposed by the kernel. As the kernel is ++ updated, previously missing attributes systemd-udevd.service is checking might ++ appear, which affects older name derivation algorithms, too. + + + +diff --git a/meson.build b/meson.build +index 65c1d0785e..57de947367 100644 +--- a/meson.build ++++ b/meson.build +@@ -639,6 +639,9 @@ else + conf.set('DEFAULT_HIERARCHY', 'CGROUP_UNIFIED_ALL') + endif + ++default_net_naming_scheme = get_option('default-net-naming-scheme') ++conf.set_quoted('DEFAULT_NET_NAMING_SCHEME', default_net_naming_scheme) ++ + time_epoch = get_option('time-epoch') + if time_epoch == '' + NEWS = files('NEWS') +@@ -2925,6 +2928,7 @@ status = [ + 'default DNSSEC mode: @0@'.format(default_dnssec), + 'default DNS-over-TLS mode: @0@'.format(default_dns_over_tls), + 'default cgroup hierarchy: @0@'.format(default_hierarchy), ++ 'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme), + 'default KillUserProcesses setting: @0@'.format(kill_user_processes)] + + alt_dns_servers = '\n '.join(dns_servers.split(' ')) +diff --git a/meson_options.txt b/meson_options.txt +index 0996891177..213079ac15 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -158,6 +158,9 @@ option('default-hierarchy', type : 'combo', + description : 'default cgroup hierarchy') + option('time-epoch', type : 'string', + description : 'time epoch for time clients') ++option('default-net-naming-scheme', type : 'combo', ++ choices : ['rhel-8.0', 'rhel-8.1', 'rhel-8.2', 'rhel-8.3', 'latest'], ++ description : 'default net.naming-scheme= value') + option('system-uid-max', type : 'string', + description : 'maximum system UID') + option('system-gid-max', type : 'string', +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index 148696183e..d85dc2848b 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -185,6 +185,19 @@ struct virtfn_info { + char suffix[IFNAMSIZ]; + }; + ++static const NamingScheme* naming_scheme_from_name(const char *name) { ++ size_t i; ++ ++ if (streq(name, "latest")) ++ return naming_schemes + ELEMENTSOF(naming_schemes) - 1; ++ ++ for (i = 0; i < ELEMENTSOF(naming_schemes); i++) ++ if (streq(naming_schemes[i].name, name)) ++ return naming_schemes + i; ++ ++ return NULL; ++} ++ + static const NamingScheme* naming_scheme(void) { + static const NamingScheme *cache = NULL; + _cleanup_free_ char *buffer = NULL; +@@ -208,25 +221,18 @@ static const NamingScheme* naming_scheme(void) { + k = buffer; + + if (k) { +- size_t i; +- +- for (i = 0; i < ELEMENTSOF(naming_schemes); i++) +- if (streq(naming_schemes[i].name, k)) { +- cache = naming_schemes + i; +- break; +- } ++ cache = naming_scheme_from_name(k); ++ if (cache) { ++ log_info("Using interface naming scheme '%s'.", cache->name); ++ return cache; ++ } + +- if (!cache) +- log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k); ++ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k); + } + +- if (cache) +- log_info("Using interface naming scheme '%s'.", cache->name); +- else { +- /* RHEL-only: here we differ from the upstream and if no naming scheme was selected we default to naming from systemd-239 */ +- cache = &naming_schemes[2]; +- log_info("Using default interface naming scheme '%s'.", cache->name); +- } ++ cache = naming_scheme_from_name(DEFAULT_NET_NAMING_SCHEME); ++ assert(cache); ++ log_info("Using default interface naming scheme '%s'.", cache->name); + + return cache; + } diff --git a/SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch b/SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch new file mode 100644 index 0000000..be9f964 --- /dev/null +++ b/SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch @@ -0,0 +1,522 @@ +From af528dcffaab1efea760395cc6676fe4b01e89b5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 9 May 2019 12:34:30 +0200 +Subject: [PATCH] man: describe naming schemes in a new man page + +I decided to make this a separate man page because it is freakin' long. +This content could equally well go in systemd-udevd.service(8), systemd.link(5), +or a new man page for the net_id builtin. + +v2: +- rename to systemd.net-naming-scheme +- add udevadm test-builtin net_id example + +(cherry picked from commit 0b1e5b6ed8c6b9a2bc53709eb75e381d360f05bf) + +Related: #1827462 + +[msekleta: I've removed parts that describe features which are not +available in RHEL-8] +--- + man/rules/meson.build | 1 + + man/systemd-udevd.service.xml | 19 +- + man/systemd.link.xml | 10 +- + man/systemd.net-naming-scheme.xml | 385 ++++++++++++++++++++++++++++++ + src/udev/udev-builtin-net_id.c | 1 + + 5 files changed, 402 insertions(+), 14 deletions(-) + create mode 100644 man/systemd.net-naming-scheme.xml + +diff --git a/man/rules/meson.build b/man/rules/meson.build +index 7ae94ea265..e6c0a99bbd 100644 +--- a/man/rules/meson.build ++++ b/man/rules/meson.build +@@ -714,6 +714,7 @@ manpages = [ + ['systemd.kill', '5', [], ''], + ['systemd.link', '5', [], ''], + ['systemd.mount', '5', [], ''], ++ ['systemd.net-naming-scheme', '7', [], ''], + ['systemd.netdev', '5', [], 'ENABLE_NETWORKD'], + ['systemd.network', '5', [], 'ENABLE_NETWORKD'], + ['systemd.nspawn', '5', [], ''], +diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml +index b738591c93..f4cdb2f1e7 100644 +--- a/man/systemd-udevd.service.xml ++++ b/man/systemd-udevd.service.xml +@@ -174,15 +174,11 @@ + net.naming-scheme= + + Network interfaces are renamed to give them predictable names when possible (unless +- net.ifnames=0 is specified, see above). The names are derived from various +- device metadata fields. Newer versions of systemd-udevd.service take more of +- these fields into account, improving (and thus possibly changing) the names used for the same +- devices. With this kernel command line option it is possible to pick a specific version of this +- algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers +- are known: rhel-8.0, rhel-8.1, rhel-8.2, +- rhel-8.3 which each implement the naming scheme that was the default in the +- indicated Red Hat Enterprise Linux minor version. In addition, latest may be +- used to designate the latest scheme known (to this particular version of ++ net.ifnames=0 is specified, see above). With this kernel command line option it ++ is possible to pick a specific version of this algorithm and override the default chosen at ++ compilation time. Expects one of the naming scheme identifiers listed in ++ systemd.net-naming-scheme7, ++ or latest to select the latest scheme known (to this particular version of + systemd-udevd.service). + Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: + the naming is generally derived from driver attributes exposed by the kernel. As the kernel is +@@ -191,9 +187,8 @@ + + + +- +- ++ ++ + + + See Also +diff --git a/man/systemd.link.xml b/man/systemd.link.xml +index 6708753e82..32657308d0 100644 +--- a/man/systemd.link.xml ++++ b/man/systemd.link.xml +@@ -286,6 +286,7 @@ + The name is set based on information given by + the firmware for on-board devices, as exported by the + udev property ID_NET_NAME_ONBOARD. ++ See systemd.net-naming-scheme7. + + + +@@ -295,6 +296,7 @@ + The name is set based on information given by + the firmware for hot-plug devices, as exported by the + udev property ID_NET_NAME_SLOT. ++ See systemd.net-naming-scheme7. + + + +@@ -303,7 +305,9 @@ + + The name is set based on the device's physical + location, as exported by the udev property +- ID_NET_NAME_PATH. ++ ID_NET_NAME_PATH. ++ See systemd.net-naming-scheme7. ++ + + + +@@ -311,7 +315,9 @@ + + The name is set based on the device's persistent + MAC address, as exported by the udev property +- ID_NET_NAME_MAC. ++ ID_NET_NAME_MAC. ++ See systemd.net-naming-scheme7. ++ + + + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +new file mode 100644 +index 0000000000..a12cc3c460 +--- /dev/null ++++ b/man/systemd.net-naming-scheme.xml +@@ -0,0 +1,385 @@ ++ ++ ++ ++ ++ ++ ++ systemd.net-naming-scheme ++ systemd ++ ++ ++ ++ systemd.net-naming-scheme ++ 7 ++ ++ ++ ++ systemd.net-naming-scheme ++ Network device naming schemes ++ ++ ++ ++ Description ++ ++ Network interfaces may be renamed to give them predictable names when there's enough information to ++ generate appropriate names and the use of certain types of names is configured. This page describes the ++ first part, i.e. what possible names may be generated. Those names are generated by the ++ systemd-udevd.service8 ++ builtin net_id and exported as udev properties ++ (ID_NET_NAME_ONBOARD=, ID_NET_LABEL_ONBOARD=, ++ ID_NET_NAME_PATH=, ID_NET_NAME_SLOT=). ++ ++ Names are derived from various device metadata attributes. Newer versions of udev take more of ++ these attributes into account, improving (and thus possibly changing) the names used for the same ++ devices. Differents version of the naming rules are called "naming schemes". The default naming scheme is ++ chosen at compilation time. Usually this will be the latest implemented version, but it is also possible ++ to set one of the older versions to preserve compatibility. This may be useful for example for ++ distributions, which may introduce new versions of systemd in stable releases without changing the naming ++ scheme. The naming scheme may also be overriden using the net.naming-scheme= kernel ++ command line switch, see ++ systemd-udevd.service8. ++ Available naming schemes are described below. ++ ++ After the udev proprties have been generated, appropriate udev rules may be used to actually rename ++ devices based on those properties. See the description of NamePolicy= in ++ systemd.link5. ++ ++ ++ ++ ++ Naming ++ ++ All names start with a two-character prefix that signifies the interface type. ++ ++ ++ Two character prefixes based on the type of interface ++ ++ ++ ++ ++ Prefix ++ Description ++ ++ ++ ++ ++ en ++ Ethernet ++ ++ ++ sl ++ serial line IP (slip) ++ ++ ++ wl ++ Wireless local area network (WLAN) ++ ++ ++ ww ++ Wireless wide area network (WWAN) ++ ++ ++ ++
++ ++ The udev net_id builtin exports the following udev device properties: ++ ++ ++ ++ ID_NET_NAME_ONBOARD=prefixonumber ++ ++ This name is set based on the ordering information given by the firmware for ++ on-board devices. The name consists of the prefix, letter o, and a number ++ specified by the firmware. This is only available for PCI devices. ++ ++ ++ ++ ++ ID_NET_LABEL_ONBOARD=prefix label ++ ++ This property is set based on label given by the firmware for on-board devices. The ++ name consists of the prefix concatenated with the label. This is only available for PCI devices. ++ ++ ++ ++ ++ ++ ID_NET_NAME_MAC=prefixxAABBCCDDEEFF ++ ++ This name consists of the prefix, letter x, and 12 hexadecimal ++ digits of the MAC address. It is available if the device has a fixed MAC address. Because this name ++ is based on an attribute of the card itself, it remains "stable" when the device is moved (even ++ between machines), but will change when the hardware is replaced. ++ ++ ++ ++ ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port] ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]bnumber ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]uport…[cconfig][iinterface] ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]vslot ++ ++ This property describes the slot position. Different schemes are used depending on ++ the bus type, as described in the table below. In all cases, PCI slot information must be known. In ++ case of USB, BCMA, and SR-VIO devices, the full name consists of the prefix, PCI slot identifier, ++ and USB or BCMA or SR-VIO slot identifier. The first two parts are denoted as "…" in the table ++ below. ++ ++ ++ Slot naming schemes ++ ++ ++ ++ ++ Format ++ Description ++ ++ ++ ++ ++ ++ prefix [Pdomainsslot [ffunction] [nport_name | ddev_port] ++ PCI slot number ++ ++ ++ ++ … bnumber ++ Broadcom bus (BCMA) core number ++ ++ ++ ++ … uport… [cconfig] [iinterface] ++ USB port number chain ++ ++ ++ ++ … vslot ++ SR-VIO slot number ++ ++ ++ ++
++ ++ The PCI domain is only prepended when it is not 0. All multi-function PCI devices will carry ++ the ffunction number in the device name, including ++ the function 0 device. For non-multi-function devices, the number is suppressed if 0. The port name ++ port_name is used, or the port number ++ ddev_port if the name is not known. ++ ++ For BCMA devices, the core number is suppressed when 0. ++ ++ For USB devices the full chain of port numbers of hubs is composed. If the name gets longer ++ than the maximum number of 15 characters, the name is not exported. The usual USB configuration ++ number 1 and interface number 0 values are suppressed. ++
++ ++ SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of ++ v and the virtual device number, with any leading zeros removed. The bus ++ number is ignored. This device type is found in IBM PowerVMs. ++
++ ++ ++ ID_NET_NAME_PATH=prefixcbus_id ++ ID_NET_NAME_PATH=prefixavendormodeliinstance ++ ID_NET_NAME_PATH=prefixiaddressnport_name ++ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port] ++ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]bnumber ++ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]uport…[cconfig][iinterface] ++ ++ This property describes the device installation location. Different schemes are ++ used depending on the bus type, as described in the table below. For BCMA and USB devices, PCI path ++ information must known, and the full name consists of the prefix, PCI slot identifier, and USB or ++ BCMA location. The first two parts are denoted as "…" in the table below. ++ ++ ++ Path naming schemes ++ ++ ++ ++ ++ Format ++ Description ++ ++ ++ ++ ++ ++ prefix cbus_id ++ CCW or grouped CCW device identifier ++ ++ ++ ++ prefix avendor model iinstance ++ ACPI path names for ARM64 platform devices ++ ++ ++ ++ prefix [Pdomainpbus sslot [ffunction] [nphys_port_name | ddev_port] ++ PCI geographical location ++ ++ ++ ++ … bnumber ++ Broadcom bus (BCMA) core number ++ ++ ++ ++ … uport… [cconfig] [iinterface] ++ USB port number chain ++ ++ ++ ++ ++
++ ++ CCW and grouped CCW devices are found in IBM System Z mainframes. Any leading zeros and ++ dots are suppressed. ++ ++ For PCI, BCMA, and USB devices, the same rules as described above for slot naming are ++ used. ++
++
++
++
++ ++ ++ History ++ ++ The following "naming schemes" have been defined: ++ ++ ++ ++ rhel-8.0 ++ ++ Naming was changed for virtual network interfaces created with SR-IOV and NPAR and ++ for devices where the PCI network controller device does not have a slot number associated. ++ ++ SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of ++ vport, where port is the ++ virtual device number. Previously those virtual devices were named as if completely independent. ++ ++ ++ The ninth and later NPAR virtual devices are named following the scheme used for the first ++ eight NPAR partitions. Previously those devices were not renamed and the kernel default ++ ("ethN") was used. ++ ++ Names are also generated for PCI devices where the PCI network controller device does not ++ have an associated slot number itself, but one of its parents does. Previously those devices were ++ not renamed and the kernel default was used. ++ ++ ++ ++ ++ rhel-8.1 ++ ++ Same as naming scheme rhel-8.0. ++ ++ ++ ++ rhel-8.2 ++ ++ Same as naming scheme rhel-8.0. ++ ++ ++ ++ rhel-8.3 ++ ++ Same as naming scheme rhel-8.0. ++ ++ ++ Note that latest may be used to denote the latest scheme known (to this ++ particular version of systemd. ++ ++ ++ ++ ++ Examples ++ ++ ++ Using <command>udevadm test-builtin</command> to display device properties ++ ++ $ udevadm test-builtin net_id /sys/class/net/enp0s31f6 ++... ++Using default interface naming scheme 'rhel-8.3'. ++ID_NET_NAMING_SCHEME=rhel-8.3 ++ID_NET_NAME_MAC=enx54ee75cb1dc0 ++ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. ++ID_NET_NAME_PATH=enp0s31f6 ++... ++ ++ ++ ++ PCI Ethernet card with firmware index "1" ++ ++ ID_NET_NAME_ONBOARD=eno1 ++ID_NET_NAME_ONBOARD_LABEL=enEthernet Port 1 ++ ++ ++ ++ ++ ++ PCI Ethernet card in hotplug slot with firmware index number ++ ++ # /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1 ++ID_NET_NAME_MAC=enx000000000466 ++ID_NET_NAME_PATH=enp5s0 ++ID_NET_NAME_SLOT=ens1 ++ ++ ++ ++ PCI Ethernet multi-function card with 2 ports ++ ++ # /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 ++ID_NET_NAME_MAC=enx78e7d1ea46da ++ID_NET_NAME_PATH=enp2s0f0 ++ ++# /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 ++ID_NET_NAME_MAC=enx78e7d1ea46dc ++ID_NET_NAME_PATH=enp2s0f1 ++ ++ ++ ++ PCI WLAN card ++ ++ # /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 ++ID_NET_NAME_MAC=wlx0024d7e31130 ++ID_NET_NAME_PATH=wlp3s0 ++ ++ ++ ++ USB built-in 3G modem ++ ++ # /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 ++ID_NET_NAME_MAC=wwx028037ec0200 ++ID_NET_NAME_PATH=wwp0s29u1u4i6 ++ ++ ++ ++ USB Android phone ++ ++ # /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 ++ID_NET_NAME_MAC=enxd626b3450fb5 ++ID_NET_NAME_PATH=enp0s29u1u2 ++ ++ ++ ++ s390 grouped CCW interface ++ ++ # /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 ++ID_NET_NAME_MAC=enx026d3c00000a ++ID_NET_NAME_PATH=encf5f0 ++ ++ ++ ++ ++ See Also ++ ++ udev7, ++ udevadm8, ++ the ++ original page describing stable interface names ++ ++ ++ ++
+diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index d85dc2848b..aa553d5ade 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -78,6 +78,7 @@ + * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 + * ID_NET_NAME_MAC=enx026d3c00000a + * ID_NET_NAME_PATH=encf5f0 ++ * When the code here is changed, man/systemd.net-naming-scheme.xml must be updated too. + */ + + #include diff --git a/SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch b/SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch new file mode 100644 index 0000000..ea6dfa2 --- /dev/null +++ b/SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch @@ -0,0 +1,51 @@ +From 462420bc7ea22a05bfc2d021d395aade2b8ee7dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 19 Oct 2020 10:56:11 +0200 +Subject: [PATCH] udev/net_id: parse _SUN ACPI index as a signed integer + +Negative value means there is no match between a PCI device and any of +the slots. In the following commit we will extend this and value of 0 +will indicate that there is a match between some slot and PCI device, +but that device is a PCI bridge. + +(cherry picked from commit 3e545ae5abcf258791eacbee60c829c100a33274) + +Related: #1827462 +--- + src/udev/udev-builtin-net_id.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index aa553d5ade..ede24dee41 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -391,7 +391,8 @@ static bool is_pci_ari_enabled(struct udev_device *dev) { + + static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + struct udev *udev = udev_device_get_udev(names->pcidev); +- unsigned domain, bus, slot, func, dev_port = 0, hotplug_slot = 0; ++ unsigned domain, bus, slot, func, dev_port = 0; ++ int hotplug_slot = -1; + size_t l; + char *s; + const char *attr, *port_name; +@@ -449,15 +450,15 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + hotplug_slot_dev = names->pcidev; + while (hotplug_slot_dev) { + FOREACH_DIRENT_ALL(dent, dir, break) { +- unsigned i; +- int r; ++ int i, r; + char str[PATH_MAX]; + _cleanup_free_ char *address = NULL; + + if (dent->d_name[0] == '.') + continue; +- r = safe_atou_full(dent->d_name, 10, &i); +- if (i < 1 || r < 0) ++ ++ r = safe_atoi(dent->d_name, &i); ++ if (r < 0 || i <= 0) + continue; + + if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) && diff --git a/SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch b/SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch new file mode 100644 index 0000000..2bd2478 --- /dev/null +++ b/SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch @@ -0,0 +1,124 @@ +From bb6114af097da0cd9c5081e42db718559130687f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 19 Oct 2020 11:10:31 +0200 +Subject: [PATCH] udev/net_id: don't generate slot based names if multiple + devices might claim the same slot + +(cherry picked from commit 2c8ec0095e6fd2e72879d4915ff8a9e5c0664d0b) + +Resolves: #1827462 +--- + man/systemd.net-naming-scheme.xml | 15 ++++++++++- + src/udev/udev-builtin-net_id.c | 41 ++++++++++++++++++++++++++----- + 2 files changed, 49 insertions(+), 7 deletions(-) + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +index a12cc3c460..10e71dcb15 100644 +--- a/man/systemd.net-naming-scheme.xml ++++ b/man/systemd.net-naming-scheme.xml +@@ -176,7 +176,10 @@ + + SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of + v and the virtual device number, with any leading zeros removed. The bus +- number is ignored. This device type is found in IBM PowerVMs. ++ number is ignored. ++ ++ In some configurations a parent PCI bridge of a given network controller may be associated ++ with a slot. In such case we don't generate this device property to avoid possible naming conflicts. + + + +@@ -288,6 +291,16 @@ + Same as naming scheme rhel-8.0. + + ++ ++ rhel-8.4 ++ ++ If the PCI slot is assocated with PCI bridge and that has multiple child network ++ controllers then all of them might derive the same value of ID_NET_NAME_SLOT ++ property. That could cause naming conflict if the property is selected as a device name. Now, we detect the ++ situation, slot - bridge relation, and we don't produce the ID_NET_NAME_SLOT property to ++ avoid possible naming conflict. ++ ++ + Note that latest may be used to denote the latest scheme known (to this + particular version of systemd. + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index ede24dee41..d8c56b62bb 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -124,6 +124,7 @@ typedef enum NamingSchemeFlags { + /* First, the individual features */ + NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/ + NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */ ++ NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */ + + /* And now the masks that combine the features above */ + NAMING_V238 = 0, +@@ -132,6 +133,7 @@ typedef enum NamingSchemeFlags { + NAMING_RHEL_8_1 = NAMING_V239, + NAMING_RHEL_8_2 = NAMING_V239, + NAMING_RHEL_8_3 = NAMING_V239, ++ NAMING_RHEL_8_4 = NAMING_V239|NAMING_BRIDGE_NO_SLOT, + + _NAMING_SCHEME_FLAGS_INVALID = -1, + } NamingSchemeFlags; +@@ -389,6 +391,26 @@ static bool is_pci_ari_enabled(struct udev_device *dev) { + return streq_ptr(udev_device_get_sysattr_value(dev, "ari_enabled"), "1"); + } + ++static bool is_pci_bridge(struct udev_device *dev) { ++ const char *v, *p; ++ ++ v = udev_device_get_sysattr_value(dev, "modalias"); ++ if (!v) ++ return false; ++ ++ if (!startswith(v, "pci:")) ++ return false; ++ ++ p = strrchr(v, 's'); ++ if (!p) ++ return false; ++ if (p[1] != 'c') ++ return false; ++ ++ /* PCI device subclass 04 corresponds to PCI bridge */ ++ return strneq(p + 2, "04", 2); ++} ++ + static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + struct udev *udev = udev_device_get_udev(names->pcidev); + unsigned domain, bus, slot, func, dev_port = 0; +@@ -461,16 +483,23 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + if (r < 0 || i <= 0) + continue; + ++ /* match slot address with device by stripping the function */ + if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) && +- read_one_line_file(str, &address) >= 0) +- /* match slot address with device by stripping the function */ +- if (startswith(udev_device_get_sysname(hotplug_slot_dev), address)) +- hotplug_slot = i; ++ read_one_line_file(str, &address) >= 0 && ++ startswith(udev_device_get_sysname(hotplug_slot_dev), address)) { ++ hotplug_slot = i; ++ ++ /* We found the match between PCI device and slot. However, we won't use the ++ * slot index if the device is a PCI bridge, because it can have other child ++ * devices that will try to claim the same index and that would create name ++ * collision. */ ++ if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev)) ++ hotplug_slot = 0; + +- if (hotplug_slot > 0) + break; ++ } + } +- if (hotplug_slot > 0) ++ if (hotplug_slot >= 0) + break; + rewinddir(dir); + hotplug_slot_dev = udev_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL); diff --git a/SOURCES/0481-fix-typo-in-ProtectSystem-option.patch b/SOURCES/0481-fix-typo-in-ProtectSystem-option.patch new file mode 100644 index 0000000..06f5f4c --- /dev/null +++ b/SOURCES/0481-fix-typo-in-ProtectSystem-option.patch @@ -0,0 +1,25 @@ +From 573229efeb2c5ade25794deee8cfe2f967414ef7 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Fri, 6 Nov 2020 10:13:19 +0100 +Subject: [PATCH] fix typo in ProtectSystem= option + +This was introduced by commit d9ae3222cfbd5d2a48e6dbade6617085cc76f1c1 . + +Resolves: #1871139 +--- + units/systemd-resolved.service.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in +index aad1a53a5f..f10f1d1690 100644 +--- a/units/systemd-resolved.service.in ++++ b/units/systemd-resolved.service.in +@@ -30,7 +30,7 @@ CapabilityBoundingSet=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + AmbientCapabilities=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + PrivateTmp=yes + PrivateDevices=yes +-ProtectSystems=strict ++ProtectSystem=strict + ProtectHome=yes + ProtectControlGroups=yes + ProtectKernelTunables=yes diff --git a/SOURCES/0482-remove-references-of-non-existent-man-pages.patch b/SOURCES/0482-remove-references-of-non-existent-man-pages.patch new file mode 100644 index 0000000..31ae5e0 --- /dev/null +++ b/SOURCES/0482-remove-references-of-non-existent-man-pages.patch @@ -0,0 +1,34 @@ +From 5e74048399c4610da27b5f7fbbb53784030aeb70 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Mon, 9 Nov 2020 09:27:02 +0100 +Subject: [PATCH] remove references of non-existent man pages + +This is a follow-up to commit 8ad89170001c9aba8849630ddb5da81d9e24a1bc, +which introduced the man page change. + +Resolves: #1876807 +--- + man/systemd.special.xml | 10 ---------- + 1 file changed, 10 deletions(-) + +diff --git a/man/systemd.special.xml b/man/systemd.special.xml +index c9d4345016..fe6324a4a0 100644 +--- a/man/systemd.special.xml ++++ b/man/systemd.special.xml +@@ -657,16 +657,6 @@ + target unit and pull in the target from it, also with Requires=. Note that by default this + target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by + units that want to run only on successful boots. +- +- See +- systemd-boot-check-no-failures.service8 +- for a service that implements a generic system health check and orders itself before +- boot-complete.target. +- +- See +- systemd-bless-boot.service8 +- for a service that propagates boot success information to the boot loader, and orders itself after +- boot-complete.target. + + + diff --git a/SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch b/SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch new file mode 100644 index 0000000..72141ba --- /dev/null +++ b/SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch @@ -0,0 +1,91 @@ +From b14c82dd9f9fcc42810614cf02efe8651897d36f Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Wed, 10 Jun 2020 20:19:41 +0200 +Subject: [PATCH] log: Prefer logging to CLI unless JOURNAL_STREAM is set + +(cherry picked from commit bc694c06e60505efeb09e5278a7b22cdfa23975e) + +Resolves: #1865840 +--- + src/basic/log.c | 32 +++++++++++++++++++++++++++++--- + test/TEST-21-SYSUSERS/test.sh | 3 +-- + 2 files changed, 30 insertions(+), 5 deletions(-) + +diff --git a/src/basic/log.c b/src/basic/log.c +index 48c094b548..9387e56a57 100644 +--- a/src/basic/log.c ++++ b/src/basic/log.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -19,6 +20,7 @@ + #include "sd-messages.h" + + #include "alloc-util.h" ++#include "extract-word.h" + #include "fd-util.h" + #include "format-util.h" + #include "io-util.h" +@@ -220,6 +222,32 @@ fail: + return r; + } + ++static bool stderr_is_journal(void) { ++ _cleanup_free_ char *w = NULL; ++ const char *e; ++ uint64_t dev, ino; ++ struct stat st; ++ ++ e = getenv("JOURNAL_STREAM"); ++ if (!e) ++ return false; ++ ++ if (extract_first_word(&e, &w, ":", EXTRACT_DONT_COALESCE_SEPARATORS) <= 0) ++ return false; ++ if (!e) ++ return false; ++ ++ if (safe_atou64(w, &dev) < 0) ++ return false; ++ if (safe_atou64(e, &ino) < 0) ++ return false; ++ ++ if (fstat(STDERR_FILENO, &st) < 0) ++ return false; ++ ++ return st.st_dev == dev && st.st_ino == ino; ++} ++ + int log_open(void) { + int r; + +@@ -239,9 +267,7 @@ int log_open(void) { + return 0; + } + +- if (log_target != LOG_TARGET_AUTO || +- getpid_cached() == 1 || +- isatty(STDERR_FILENO) <= 0) { ++ if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || stderr_is_journal()) { + + if (!prohibit_ipc && + IN_SET(log_target, LOG_TARGET_AUTO, +diff --git a/test/TEST-21-SYSUSERS/test.sh b/test/TEST-21-SYSUSERS/test.sh +index b1049e720d..3460d71f22 100755 +--- a/test/TEST-21-SYSUSERS/test.sh ++++ b/test/TEST-21-SYSUSERS/test.sh +@@ -108,8 +108,7 @@ test_run() { + echo "*** Running test $f" + prepare_testdir ${f%.input} + cp $f $TESTDIR/usr/lib/sysusers.d/test.conf +- systemd-sysusers --root=$TESTDIR 2> /dev/null +- journalctl -t systemd-sysusers -o cat | tail -n1 > $TESTDIR/tmp/err ++ systemd-sysusers --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/tmp/err + if ! diff -u $TESTDIR/tmp/err ${f%.*}.expected-err; then + echo "**** Unexpected error output for $f" + cat $TESTDIR/tmp/err diff --git a/SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch b/SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch new file mode 100644 index 0000000..902a685 --- /dev/null +++ b/SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch @@ -0,0 +1,58 @@ +From f0d9e0cb24958bc11c8d83f0a3de651def2aa1d6 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 30 Apr 2020 18:30:56 +0200 +Subject: [PATCH] locale-util: add new helper locale_is_installed() + +This new helper checks whether the specified locale is installed. It's +distinct from locale_is_valid() which just superficially checks if a +string looks like something that could be a valid locale. + +Heavily inspired by @jsynacek's #13964. + +Replaces: #13964 +(cherry picked from commit 23fa786ca67ed3a32930ff1a7b175ac823db187c) + +Related: #1755287 +--- + src/basic/locale-util.c | 15 +++++++++++++++ + src/basic/locale-util.h | 1 + + 2 files changed, 16 insertions(+) + +diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c +index 7cd143ea6f..42ef309ebd 100644 +--- a/src/basic/locale-util.c ++++ b/src/basic/locale-util.c +@@ -204,6 +204,21 @@ bool locale_is_valid(const char *name) { + return true; + } + ++int locale_is_installed(const char *name) { ++ if (!locale_is_valid(name)) ++ return false; ++ ++ if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */ ++ return true; ++ ++ _cleanup_(freelocalep) locale_t loc = ++ newlocale(LC_ALL_MASK, name, 0); ++ if (loc == (locale_t) 0) ++ return errno == ENOMEM ? -ENOMEM : false; ++ ++ return true; ++} ++ + void init_gettext(void) { + setlocale(LC_ALL, ""); + textdomain(GETTEXT_PACKAGE); +diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h +index 368675f286..b40f9c641a 100644 +--- a/src/basic/locale-util.h ++++ b/src/basic/locale-util.h +@@ -31,6 +31,7 @@ typedef enum LocaleVariable { + + int get_locales(char ***l); + bool locale_is_valid(const char *name); ++int locale_is_installed(const char *name); + + #define _(String) gettext(String) + #define N_(String) String diff --git a/SOURCES/0485-test-add-test-case-for-locale_is_installed.patch b/SOURCES/0485-test-add-test-case-for-locale_is_installed.patch new file mode 100644 index 0000000..57abbe8 --- /dev/null +++ b/SOURCES/0485-test-add-test-case-for-locale_is_installed.patch @@ -0,0 +1,53 @@ +From 3d08c7971a80370f60dd14b068779851e0f82c24 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 30 Apr 2020 18:32:55 +0200 +Subject: [PATCH] test: add test case for locale_is_installed() + +(cherry picked from commit b45b0a69bb7ef3e6e66d443eae366b6d1c387cab) + +Related: #1755287 +--- + src/test/test-locale-util.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c +index 0c3f6a62ed..0d50c33ce5 100644 +--- a/src/test/test-locale-util.c ++++ b/src/test/test-locale-util.c +@@ -34,6 +34,28 @@ static void test_locale_is_valid(void) { + assert_se(!locale_is_valid("\x01gar\x02 bage\x03")); + } + ++static void test_locale_is_installed(void) { ++ log_info("/* %s */", __func__); ++ ++ /* Always available */ ++ assert_se(locale_is_installed("POSIX") > 0); ++ assert_se(locale_is_installed("C") > 0); ++ ++ /* Might, or might not be installed. */ ++ assert_se(locale_is_installed("en_EN.utf8") >= 0); ++ assert_se(locale_is_installed("fr_FR.utf8") >= 0); ++ assert_se(locale_is_installed("fr_FR@euro") >= 0); ++ assert_se(locale_is_installed("fi_FI") >= 0); ++ ++ /* Definitely not valid */ ++ assert_se(locale_is_installed("") == 0); ++ assert_se(locale_is_installed("/usr/bin/foo") == 0); ++ assert_se(locale_is_installed("\x01gar\x02 bage\x03") == 0); ++ ++ /* Definitely not installed */ ++ assert_se(locale_is_installed("zz_ZZ") == 0); ++} ++ + static void test_keymaps(void) { + _cleanup_strv_free_ char **kmaps = NULL; + char **p; +@@ -95,6 +117,7 @@ static void dump_special_glyphs(void) { + int main(int argc, char *argv[]) { + test_get_locales(); + test_locale_is_valid(); ++ test_locale_is_installed(); + test_keymaps(); + + dump_special_glyphs(); diff --git a/SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch b/SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch new file mode 100644 index 0000000..cb4ec8d --- /dev/null +++ b/SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch @@ -0,0 +1,232 @@ +From 5813180a75aa1ef90f6d3459fc5beb099b815cfb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 30 Apr 2020 18:32:44 +0200 +Subject: [PATCH] tree-wide: port various bits over to locale_is_installed() + +(cherry picked from commit a00a78b84e2ab352b3144bfae8bc578d172303be) + +Resolves: #1755287 +--- + src/firstboot/firstboot.c | 30 ++++++++------ + src/locale/localed.c | 87 ++++++++++++++++++++++++--------------- + 2 files changed, 71 insertions(+), 46 deletions(-) + +diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c +index a98e53b3a3..7e177a50fa 100644 +--- a/src/firstboot/firstboot.c ++++ b/src/firstboot/firstboot.c +@@ -192,6 +192,14 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char * + } + } + ++static bool locale_is_ok(const char *name) { ++ ++ if (arg_root) ++ return locale_is_valid(name); ++ ++ return locale_is_installed(name) > 0; ++} ++ + static int prompt_locale(void) { + _cleanup_strv_free_ char **locales = NULL; + int r; +@@ -215,14 +223,14 @@ static int prompt_locale(void) { + + putchar('\n'); + +- r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale); ++ r = prompt_loop("Please enter system locale name or number", locales, locale_is_ok, &arg_locale); + if (r < 0) + return r; + + if (isempty(arg_locale)) + return 0; + +- r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages); ++ r = prompt_loop("Please enter system message locale name or number", locales, locale_is_ok, &arg_locale_messages); + if (r < 0) + return r; + +@@ -780,11 +788,6 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_LOCALE: +- if (!locale_is_valid(optarg)) { +- log_error("Locale %s is not valid.", optarg); +- return -EINVAL; +- } +- + r = free_and_strdup(&arg_locale, optarg); + if (r < 0) + return log_oom(); +@@ -792,11 +795,6 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_LOCALE_MESSAGES: +- if (!locale_is_valid(optarg)) { +- log_error("Locale %s is not valid.", optarg); +- return -EINVAL; +- } +- + r = free_and_strdup(&arg_locale_messages, optarg); + if (r < 0) + return log_oom(); +@@ -922,6 +920,14 @@ static int parse_argv(int argc, char *argv[]) { + assert_not_reached("Unhandled option"); + } + ++ /* We check if the specified locale strings are valid down here, so that we can take --root= into ++ * account when looking for the locale files. */ ++ ++ if (arg_locale && !locale_is_ok(arg_locale)) ++ return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale); ++ if (arg_locale_messages && !locale_is_ok(arg_locale_messages)) ++ return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale_messages); ++ + return 1; + } + +diff --git a/src/locale/localed.c b/src/locale/localed.c +index 253973fd49..d6ed40babe 100644 +--- a/src/locale/localed.c ++++ b/src/locale/localed.c +@@ -259,18 +259,57 @@ static void locale_free(char ***l) { + (*l)[p] = mfree((*l)[p]); + } + ++static int process_locale_list_item( ++ const char *assignment, ++ char *new_locale[static _VARIABLE_LC_MAX], ++ sd_bus_error *error) { ++ ++ assert(assignment); ++ assert(new_locale); ++ ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { ++ const char *name, *e; ++ ++ assert_se(name = locale_variable_to_string(p)); ++ ++ e = startswith(assignment, name); ++ if (!e) ++ continue; ++ ++ if (*e != '=') ++ continue; ++ ++ e++; ++ ++ if (!locale_is_valid(e)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e); ++ if (locale_is_installed(e) <= 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e); ++ if (new_locale[p]) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name); ++ ++ new_locale[p] = strdup(e); ++ if (!new_locale[p]) ++ return -ENOMEM; ++ ++ return 0; ++ } ++ ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment); ++} ++ + static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + _cleanup_strv_free_ char **settings = NULL, **l = NULL; + char *new_locale[_VARIABLE_LC_MAX] = {}, **i; + _cleanup_(locale_free) _unused_ char **dummy = new_locale; + bool modified = false; +- int interactive, p, r; ++ int interactive, r; + + assert(m); + assert(c); + +- r = bus_message_read_strv_extend(m, &l); ++ r = sd_bus_message_read_strv(m, &l); + if (r < 0) + return r; + +@@ -279,11 +318,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + return r; + + /* If single locale without variable name is provided, then we assume it is LANG=. */ +- if (strv_length(l) == 1 && !strchr(*l, '=')) { +- if (!locale_is_valid(*l)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); ++ if (strv_length(l) == 1 && !strchr(l[0], '=')) { ++ if (!locale_is_valid(l[0])) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]); ++ if (locale_is_installed(l[0]) <= 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]); + +- new_locale[VARIABLE_LANG] = strdup(*l); ++ new_locale[VARIABLE_LANG] = strdup(l[0]); + if (!new_locale[VARIABLE_LANG]) + return -ENOMEM; + +@@ -292,31 +333,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + + /* Check whether a variable is valid */ + STRV_FOREACH(i, l) { +- bool valid = false; +- +- for (p = 0; p < _VARIABLE_LC_MAX; p++) { +- size_t k; +- const char *name; +- +- name = locale_variable_to_string(p); +- assert(name); +- +- k = strlen(name); +- if (startswith(*i, name) && +- (*i)[k] == '=' && +- locale_is_valid((*i) + k + 1)) { +- valid = true; +- +- new_locale[p] = strdup((*i) + k + 1); +- if (!new_locale[p]) +- return -ENOMEM; +- +- break; +- } +- } +- +- if (!valid) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); ++ r = process_locale_list_item(*i, new_locale, error); ++ if (r < 0) ++ return r; + } + + /* If LANG was specified, but not LANGUAGE, check if we should +@@ -339,7 +358,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + } + + /* Merge with the current settings */ +- for (p = 0; p < _VARIABLE_LC_MAX; p++) ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + if (!isempty(c->locale[p]) && isempty(new_locale[p])) { + new_locale[p] = strdup(c->locale[p]); + if (!new_locale[p]) +@@ -348,7 +367,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + + locale_simplify(new_locale); + +- for (p = 0; p < _VARIABLE_LC_MAX; p++) ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + if (!streq_ptr(c->locale[p], new_locale[p])) { + modified = true; + break; +@@ -373,7 +392,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + +- for (p = 0; p < _VARIABLE_LC_MAX; p++) ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + free_and_replace(c->locale[p], new_locale[p]); + + r = locale_write_data(c, &settings); diff --git a/SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch b/SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch new file mode 100644 index 0000000..363f424 --- /dev/null +++ b/SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch @@ -0,0 +1,339 @@ +From 4c41ad9418058aefb2d2732b0b65da9c7cdf5151 Mon Sep 17 00:00:00 2001 +From: Ruixin Bao +Date: Tue, 21 Aug 2018 20:40:56 +0000 +Subject: [PATCH] install: allow instantiated units to be enabled via presets + +This patch implements https://github.com/systemd/systemd/issues/9421. + +The .preset file now is able to take a rule in the format of:(e.g) +enable foo@.service bar0 bar1 bar2 + +In the above example, when preset-all is called, all three instances of +foo@bar0.service, foo@bar1.service and foo@bar2.service will be enabled. + +When preset is called on a single service(e.g: foo@bar1.service), only +the mentioned one(foo@bar1.service) will be enabled. + +Tests are added for future regression. + +(cherry picked from commit 4c9565eea534cd233a913c8c21f7920dba229743) + +Resolves: #1812972 +--- + src/shared/install.c | 155 ++++++++++++++++++++++++++++++----- + src/test/test-install-root.c | 57 +++++++++++++ + 2 files changed, 193 insertions(+), 19 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 77ae812878..1d4beaa83b 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -60,6 +60,7 @@ typedef enum { + typedef struct { + char *pattern; + PresetAction action; ++ char **instances; + } PresetRule; + + typedef struct { +@@ -87,8 +88,10 @@ static inline void presets_freep(Presets *p) { + if (!p) + return; + +- for (i = 0; i < p->n_rules; i++) ++ for (i = 0; i < p->n_rules; i++) { + free(p->rules[i].pattern); ++ strv_free(p->rules[i].instances); ++ } + + free(p->rules); + p->n_rules = 0; +@@ -2755,6 +2758,39 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char * + return 1; + } + ++static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) { ++ _cleanup_strv_free_ char **instances = NULL; ++ _cleanup_free_ char *unit_name = NULL; ++ int r; ++ ++ assert(pattern); ++ assert(out_instances); ++ assert(out_unit_name); ++ ++ r = extract_first_word(&pattern, &unit_name, NULL, 0); ++ if (r < 0) ++ return r; ++ ++ /* We handle the instances logic when unit name is extracted */ ++ if (pattern) { ++ /* We only create instances when a rule of templated unit ++ * is seen. A rule like enable foo@.service a b c will ++ * result in an array of (a, b, c) as instance names */ ++ if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) ++ return -EINVAL; ++ ++ instances = strv_split(pattern, WHITESPACE); ++ if (!instances) ++ return -ENOMEM; ++ ++ *out_instances = TAKE_PTR(instances); ++ } ++ ++ *out_unit_name = TAKE_PTR(unit_name); ++ ++ return 0; ++} ++ + static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { + _cleanup_(presets_freep) Presets ps = {}; + size_t n_allocated = 0; +@@ -2824,15 +2860,20 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres + + parameter = first_word(l, "enable"); + if (parameter) { +- char *pattern; ++ char *unit_name; ++ char **instances = NULL; + +- pattern = strdup(parameter); +- if (!pattern) +- return -ENOMEM; ++ /* Unit_name will remain the same as parameter when no instances are specified */ ++ r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances); ++ if (r < 0) { ++ log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); ++ continue; ++ } + + rule = (PresetRule) { +- .pattern = pattern, ++ .pattern = unit_name, + .action = PRESET_ENABLE, ++ .instances = instances, + }; + } + +@@ -2868,15 +2909,71 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres + return 0; + } + +-static int query_presets(const char *name, const Presets presets) { ++static int pattern_match_multiple_instances( ++ const PresetRule rule, ++ const char *unit_name, ++ char ***ret) { ++ ++ _cleanup_free_ char *templated_name = NULL; ++ int r; ++ ++ /* If no ret is needed or the rule itself does not have instances ++ * initalized, we return not matching */ ++ if (!ret || !rule.instances) ++ return 0; ++ ++ r = unit_name_template(unit_name, &templated_name); ++ if (r < 0) ++ return r; ++ if (!streq(rule.pattern, templated_name)) ++ return 0; ++ ++ /* Compose a list of specified instances when unit name is a template */ ++ if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { ++ _cleanup_free_ char *prefix = NULL; ++ _cleanup_strv_free_ char **out_strv = NULL; ++ char **iter; ++ ++ r = unit_name_to_prefix(unit_name, &prefix); ++ if (r < 0) ++ return r; ++ ++ STRV_FOREACH(iter, rule.instances) { ++ _cleanup_free_ char *name = NULL; ++ r = unit_name_build(prefix, *iter, ".service", &name); ++ if (r < 0) ++ return r; ++ r = strv_extend(&out_strv, name); ++ if (r < 0) ++ return r; ++ } ++ ++ *ret = TAKE_PTR(out_strv); ++ return 1; ++ } else { ++ /* We now know the input unit name is an instance name */ ++ _cleanup_free_ char *instance_name = NULL; ++ ++ r = unit_name_to_instance(unit_name, &instance_name); ++ if (r < 0) ++ return r; ++ ++ if (strv_find(rule.instances, instance_name)) ++ return 1; ++ } ++ return 0; ++} ++ ++static int query_presets(const char *name, const Presets presets, char ***instance_name_list) { + PresetAction action = PRESET_UNKNOWN; + size_t i; +- ++ char **s; + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + for (i = 0; i < presets.n_rules; i++) +- if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { ++ if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 || ++ fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets.rules[i].action; + break; + } +@@ -2886,7 +2983,11 @@ static int query_presets(const char *name, const Presets presets) { + log_debug("Preset files don't specify rule for %s. Enabling.", name); + return 1; + case PRESET_ENABLE: +- log_debug("Preset files say enable %s.", name); ++ if (instance_name_list && *instance_name_list) ++ STRV_FOREACH(s, *instance_name_list) ++ log_debug("Preset files say enable %s.", *s); ++ else ++ log_debug("Preset files say enable %s.", name); + return 1; + case PRESET_DISABLE: + log_debug("Preset files say disable %s.", name); +@@ -2904,7 +3005,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char + if (r < 0) + return r; + +- return query_presets(name, presets); ++ return query_presets(name, presets, NULL); + } + + static int execute_preset( +@@ -2964,6 +3065,7 @@ static int preset_prepare_one( + size_t *n_changes) { + + _cleanup_(install_context_done) InstallContext tmp = {}; ++ _cleanup_strv_free_ char **instance_name_list = NULL; + UnitFileInstallInfo *i; + int r; + +@@ -2979,19 +3081,34 @@ static int preset_prepare_one( + return 0; + } + +- r = query_presets(name, presets); ++ r = query_presets(name, presets, &instance_name_list); + if (r < 0) + return r; + + if (r > 0) { +- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; ++ if (instance_name_list) { ++ char **s; ++ STRV_FOREACH(s, instance_name_list) { ++ r = install_info_discover(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); ++ if (r < 0) ++ return r; ++ ++ r = install_info_may_process(i, paths, changes, n_changes); ++ if (r < 0) ++ return r; ++ } ++ } else { ++ r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); ++ if (r < 0) ++ return r; ++ ++ r = install_info_may_process(i, paths, changes, n_changes); ++ if (r < 0) ++ return r; ++ } + +- r = install_info_may_process(i, paths, changes, n_changes); +- if (r < 0) +- return r; + } else + r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &i, changes, n_changes); +diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c +index 15dd3c6966..dbbcfe4297 100644 +--- a/src/test/test-install-root.c ++++ b/src/test/test-install-root.c +@@ -983,6 +983,62 @@ static void test_with_dropin_template(const char *root) { + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + } + ++static void test_preset_multiple_instances(const char *root) { ++ UnitFileChange *changes = NULL; ++ size_t n_changes = 0; ++ const char *p; ++ UnitFileState state; ++ ++ /* Set up template service files and preset file */ ++ p = strjoina(root, "/usr/lib/systemd/system/foo@.service"); ++ assert_se(write_string_file(p, ++ "[Install]\n" ++ "DefaultInstance=def\n" ++ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); ++ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ ++ p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); ++ assert_se(write_string_file(p, ++ "enable foo@.service bar0 bar1 bartest\n" ++ "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */ ++ "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0); ++ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ ++ /* Preset a single instantiated unit specified in the list */ ++ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ assert_se(n_changes == 1); ++ assert_se(changes[0].type == UNIT_FILE_SYMLINK); ++ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service"); ++ assert_se(streq(changes[0].path, p)); ++ unit_file_changes_free(changes, n_changes); ++ changes = NULL; n_changes = 0; ++ ++ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); ++ assert_se(n_changes == 1); ++ assert_se(changes[0].type == UNIT_FILE_UNLINK); ++ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service"); ++ assert_se(streq(changes[0].path, p)); ++ unit_file_changes_free(changes, n_changes); ++ changes = NULL; n_changes = 0; ++ ++ /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ ++ assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); ++ assert_se(n_changes > 0); ++ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ ++} ++ + int main(int argc, char *argv[]) { + char root[] = "/tmp/rootXXXXXX"; + const char *p; +@@ -1012,6 +1068,7 @@ int main(int argc, char *argv[]) { + test_indirect(root); + test_preset_and_list(root); + test_preset_order(root); ++ test_preset_multiple_instances(root); + test_revert(root); + test_static_instance(root); + test_with_dropin(root); diff --git a/SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch b/SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch new file mode 100644 index 0000000..68e7ae2 --- /dev/null +++ b/SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch @@ -0,0 +1,127 @@ +From eacb511fc0d1e3c5857cb041ad162fb78b4381cc Mon Sep 17 00:00:00 2001 +From: Ruixin Bao +Date: Sun, 26 Aug 2018 20:00:03 +0000 +Subject: [PATCH] install: small refactor to combine two function calls into + one function + +Combine consecutive function calls of install_info_discover and +install_info_may_process into one short helper function. + +(cherry picked from commit 1e475a0ab4c46eb07f3df3fb24f5a7c3e1fa20b1) + +Related: #1812972 +--- + src/shared/install.c | 61 ++++++++++++++++++++++---------------------- + 1 file changed, 30 insertions(+), 31 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 1d4beaa83b..263b239f10 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -1676,6 +1676,25 @@ static int install_info_discover( + return r; + } + ++static int install_info_discover_and_check( ++ UnitFileScope scope, ++ InstallContext *c, ++ const LookupPaths *paths, ++ const char *name, ++ SearchFlags flags, ++ UnitFileInstallInfo **ret, ++ UnitFileChange **changes, ++ size_t *n_changes) { ++ ++ int r; ++ ++ r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes); ++ if (r < 0) ++ return r; ++ ++ return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes); ++} ++ + static int install_info_symlink_alias( + UnitFileInstallInfo *i, + const LookupPaths *paths, +@@ -2399,11 +2418,8 @@ int unit_file_add_dependency( + if (!config_path) + return -ENXIO; + +- r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &target_info, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(target_info, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &target_info, changes, n_changes); + if (r < 0) + return r; + +@@ -2412,11 +2428,8 @@ int unit_file_add_dependency( + STRV_FOREACH(f, files) { + char ***l; + +- r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(i, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + +@@ -2467,11 +2480,8 @@ int unit_file_enable( + return -ENXIO; + + STRV_FOREACH(f, files) { +- r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(i, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + +@@ -2585,10 +2595,7 @@ int unit_file_set_default( + if (r < 0) + return r; + +- r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(i, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes); + if (r < 0) + return r; + +@@ -3089,22 +3096,14 @@ static int preset_prepare_one( + if (instance_name_list) { + char **s; + STRV_FOREACH(s, instance_name_list) { +- r = install_info_discover(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- +- r = install_info_may_process(i, paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + } + } else { +- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- +- r = install_info_may_process(i, paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + } diff --git a/SOURCES/0489-test-fix-a-memleak.patch b/SOURCES/0489-test-fix-a-memleak.patch new file mode 100644 index 0000000..d01dea9 --- /dev/null +++ b/SOURCES/0489-test-fix-a-memleak.patch @@ -0,0 +1,28 @@ +From 7444c6ed3628484dfed2f204c5b78a06a50f4bd8 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 29 Aug 2018 23:27:42 +0900 +Subject: [PATCH] test: fix a memleak + +Follow-up for #9901. + +Fixes #9968. + +(cherry picked from commit efa146369398fdb73f1cd177eb2522822ebf559c) + +Related: #1812972 +--- + src/test/test-install-root.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c +index dbbcfe4297..fe1ca5b16f 100644 +--- a/src/test/test-install-root.c ++++ b/src/test/test-install-root.c +@@ -1037,6 +1037,7 @@ static void test_preset_multiple_instances(const char *root) { + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + ++ unit_file_changes_free(changes, n_changes); + } + + int main(int argc, char *argv[]) { diff --git a/SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch b/SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch new file mode 100644 index 0000000..2f726d0 --- /dev/null +++ b/SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch @@ -0,0 +1,55 @@ +From 55df2fd634f900419b718ed354132cc86cd533dd Mon Sep 17 00:00:00 2001 +From: Joerg Behrmann +Date: Tue, 10 Mar 2020 16:34:13 +0100 +Subject: [PATCH] docs: Add syntax for templated units to systemd.preset man + page + +This documents the syntax + + enable template@.service foo bar baz + +that was introduced in #9901 to preset templated units. + +(cherry picked from commit 1f667d8a7cff4355cd23ebebeb4d7179e3498eb8) + +Related: #1812972 +--- + man/systemd.preset.xml | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml +index cf807bd4c8..df401f00f3 100644 +--- a/man/systemd.preset.xml ++++ b/man/systemd.preset.xml +@@ -71,8 +71,11 @@ + either the word enable or + disable followed by a space and a unit name + (possibly with shell style wildcards), separated by newlines. +- Empty lines and lines whose first non-whitespace character is # or +- ; are ignored. ++ Empty lines and lines whose first non-whitespace character is # or ++ ; are ignored. Multiple instance names for unit ++ templates may be specified as a space separated list at the end of ++ the line instead of the customary position between @ ++ and the unit suffix. + + Presets must refer to the "real" unit file, and not to any aliases. See + systemd.unit5 +@@ -124,6 +127,17 @@ disable * + 99-, it will be read last and hence can easily + be overridden by spin or administrator preset policy. + ++ ++ Enable multiple template instances ++ ++ # /usr/lib/systemd/system-preset/80-dirsrv.preset ++ ++enable dirsrv@.service foo bar baz ++ ++ ++ This enables all three of dirsrv@foo.service, ++ dirsrv@bar.service and dirsrv@baz.service. ++ + + A GNOME spin + diff --git a/SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch b/SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch new file mode 100644 index 0000000..ea936c9 --- /dev/null +++ b/SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch @@ -0,0 +1,45 @@ +From db2816ee32fc81ba339175469e46b5dca7af8833 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 22 Aug 2020 11:58:15 +0200 +Subject: [PATCH] shared/install: fix preset operations for non-service + instantiated units + +Fixes https://github.com/coreos/ignition/issues/1064. + +(cherry picked from commit 47ab95fe4315b3f7ee5a3694460a744bb88c52fd) + +Related: #1812972 +--- + src/shared/install.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 263b239f10..c2847df3f8 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -2937,20 +2937,17 @@ static int pattern_match_multiple_instances( + + /* Compose a list of specified instances when unit name is a template */ + if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { +- _cleanup_free_ char *prefix = NULL; + _cleanup_strv_free_ char **out_strv = NULL; +- char **iter; +- +- r = unit_name_to_prefix(unit_name, &prefix); +- if (r < 0) +- return r; + ++ char **iter; + STRV_FOREACH(iter, rule.instances) { + _cleanup_free_ char *name = NULL; +- r = unit_name_build(prefix, *iter, ".service", &name); ++ ++ r = unit_name_replace_instance(unit_name, *iter, &name); + if (r < 0) + return r; +- r = strv_extend(&out_strv, name); ++ ++ r = strv_consume(&out_strv, TAKE_PTR(name)); + if (r < 0) + return r; + } diff --git a/SOURCES/0492-introduce-setsockopt_int-helper.patch b/SOURCES/0492-introduce-setsockopt_int-helper.patch new file mode 100644 index 0000000..7991184 --- /dev/null +++ b/SOURCES/0492-introduce-setsockopt_int-helper.patch @@ -0,0 +1,30 @@ +From 8cff80d7fc28ca04bd6c8e2257b46d96bea338ce Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 18 Oct 2018 19:48:18 +0200 +Subject: [PATCH] introduce setsockopt_int() helper + +As suggested by @heftig: + +https://github.com/systemd/systemd/commit/6d5e65f6454212cd400d0ebda34978a9f20cc26a#commitcomment-30938667 +(cherry picked from commit 2ff48e981e6cd1ccbfae49943274d9c8319a5e5d) + +Related: #1887181 +--- + src/basic/socket-util.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 82781a0de1..616f2e0d05 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -183,3 +183,10 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng + }) + + int socket_ioctl_fd(void); ++ ++static inline int setsockopt_int(int fd, int level, int optname, int value) { ++ if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) ++ return -errno; ++ ++ return 0; ++} diff --git a/SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch b/SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch new file mode 100644 index 0000000..aa0679b --- /dev/null +++ b/SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch @@ -0,0 +1,57 @@ +From 96681723232e9eb0182279086fef291283004806 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 27 May 2020 19:27:51 +0200 +Subject: [PATCH] socket-util: add generic socket_pass_pktinfo() helper + +The helper turns on the protocol specific "packet info" structure cmsg +for three relevant protocols we know. + +(cherry picked from commit 35a3eb9bdc95d1e6ba25bc65c78959ea104e45a1) + +Related: #1887181 +--- + src/basic/socket-util.c | 23 +++++++++++++++++++++++ + src/basic/socket-util.h | 2 ++ + 2 files changed, 25 insertions(+) + +diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c +index 986bc6e67f..053bcba670 100644 +--- a/src/basic/socket-util.c ++++ b/src/basic/socket-util.c +@@ -1246,3 +1246,26 @@ int socket_ioctl_fd(void) { + + return fd; + } ++ ++int socket_pass_pktinfo(int fd, bool b) { ++ int af; ++ socklen_t sl = sizeof(af); ++ ++ if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) ++ return -errno; ++ ++ switch (af) { ++ ++ case AF_INET: ++ return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b); ++ ++ case AF_INET6: ++ return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b); ++ ++ case AF_NETLINK: ++ return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); ++ ++ default: ++ return -EAFNOSUPPORT; ++ } ++} +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 616f2e0d05..c7c9ad34d6 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -190,3 +190,5 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { + + return 0; + } ++ ++int socket_pass_pktinfo(int fd, bool b); diff --git a/SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch b/SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch new file mode 100644 index 0000000..561e77c --- /dev/null +++ b/SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch @@ -0,0 +1,156 @@ +From 905a97ce65352d80af7260d34b74fd8342792c35 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 27 May 2020 19:36:56 +0200 +Subject: [PATCH] core: add new PassPacketInfo= socket unit property + +(cherry picked from commit a3d19f5d99c44940831a33df8b5bece4aaf749f7) + +Resolves: #1887181 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.socket.xml | 9 +++++++++ + src/core/dbus-socket.c | 4 ++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/socket.c | 8 ++++++++ + src/core/socket.h | 1 + + src/shared/bus-unit-util.c | 3 +-- + test/fuzz/fuzz-unit-file/directives.service | 1 + + 8 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 995b8797ef..de0ef9cc49 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -410,6 +410,7 @@ Most socket unit settings are available to transient units. + ✓ Broadcast= + ✓ PassCredentials= + ✓ PassSecurity= ++✓ PassPacketInfo= + ✓ TCPCongestion= + ✓ ReusePort= + ✓ MessageQueueMaxMessages= +diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml +index 8676b4e03f..a908d5b6d8 100644 +--- a/man/systemd.socket.xml ++++ b/man/systemd.socket.xml +@@ -712,6 +712,15 @@ + Defaults to . + + ++ ++ PassPacketInfo= ++ Takes a boolean value. This controls the IP_PKTINFO, ++ IPV6_RECVPKTINFO and NETLINK_PKTINFO socket options, which ++ enable reception of additional per-packet metadata as ancillary message, on ++ AF_INET, AF_INET6 and AF_UNIX sockets. ++ Defaults to . ++ ++ + + TCPCongestion= + Takes a string value. Controls the TCP +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index fa6bbe2c6f..17494b80c8 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -104,6 +104,7 @@ const sd_bus_vtable bus_socket_vtable[] = { + SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -205,6 +206,9 @@ static int bus_socket_set_transient_property( + if (streq(name, "PassSecurity")) + return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error); + ++ if (streq(name, "PassPacketInfo")) ++ return bus_set_transient_bool(u, name, &s->pass_pktinfo, message, flags, error); ++ + if (streq(name, "ReusePort")) + return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 156a4d0a6d..7d683cc84b 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -381,6 +381,7 @@ Socket.Transparent, config_parse_bool, 0, + Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) + Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) + Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) ++Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo) + Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) + Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port) + Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) +diff --git a/src/core/socket.c b/src/core/socket.c +index 97c3a7fc9a..50c32ed8f4 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -660,6 +660,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { + "%sBroadcast: %s\n" + "%sPassCredentials: %s\n" + "%sPassSecurity: %s\n" ++ "%sPassPacketInfo: %s\n" + "%sTCPCongestion: %s\n" + "%sRemoveOnStop: %s\n" + "%sWritable: %s\n" +@@ -678,6 +679,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { + prefix, yes_no(s->broadcast), + prefix, yes_no(s->pass_cred), + prefix, yes_no(s->pass_sec), ++ prefix, yes_no(s->pass_pktinfo), + prefix, strna(s->tcp_congestion), + prefix, yes_no(s->remove_on_stop), + prefix, yes_no(s->writable), +@@ -1099,6 +1101,12 @@ static void socket_apply_socket_options(Socket *s, int fd) { + log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m"); + } + ++ if (s->pass_pktinfo) { ++ r = socket_pass_pktinfo(fd, true); ++ if (r < 0) ++ log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m"); ++ } ++ + if (s->priority >= 0) + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m"); +diff --git a/src/core/socket.h b/src/core/socket.h +index b7a25d91fd..2409dbf2a0 100644 +--- a/src/core/socket.h ++++ b/src/core/socket.h +@@ -121,6 +121,7 @@ struct Socket { + bool broadcast; + bool pass_cred; + bool pass_sec; ++ bool pass_pktinfo; + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + SocketAddressBindIPv6Only bind_ipv6_only; +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index daa2c2dce5..9010448aaf 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -1478,8 +1478,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons + if (STR_IN_SET(field, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", + "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet", +- "FlushPending")) +- ++ "FlushPending", "PassPacketInfo")) + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "Priority", "IPTTL", "Mark")) +diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +index 9d0530df72..8fde27fc90 100644 +--- a/test/fuzz/fuzz-unit-file/directives.service ++++ b/test/fuzz/fuzz-unit-file/directives.service +@@ -161,6 +161,7 @@ PIDFile= + PartOf= + PassCredentials= + PassSecurity= ++PassPacketInfo= + PathChanged= + PathExists= + PathExistsGlob= diff --git a/SOURCES/0495-resolved-tweak-cmsg-calculation.patch b/SOURCES/0495-resolved-tweak-cmsg-calculation.patch new file mode 100644 index 0000000..5ce13b6 --- /dev/null +++ b/SOURCES/0495-resolved-tweak-cmsg-calculation.patch @@ -0,0 +1,30 @@ +From 6ece87bef14ac5741fc870644504737b00607546 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 27 May 2020 19:38:38 +0200 +Subject: [PATCH] resolved: tweak cmsg calculation + +We ask for the TTL, then have enough space for it. + +We probably can drop the extra cmsg space now, but let's figure that out +another time, since the extra cmsg space is used elsewhere in resolved +as well. + +(cherry picked from commit 08ab18618ec59022582f1513c0718ba369f5ba85) + +Related: #1887181 +--- + src/resolve/resolved-dns-stream.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c +index 066daef96e..555e200a23 100644 +--- a/src/resolve/resolved-dns-stream.c ++++ b/src/resolve/resolved-dns-stream.c +@@ -70,6 +70,7 @@ static int dns_stream_identify(DnsStream *s) { + union { + struct cmsghdr header; /* For alignment */ + uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) ++ + CMSG_SPACE(int) + /* for the TTL */ + + EXTRA_CMSG_SPACE /* kernel appears to require extra space */]; + } control; + struct msghdr mh = {}; diff --git a/SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch b/SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch new file mode 100644 index 0000000..cd054ce --- /dev/null +++ b/SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch @@ -0,0 +1,26 @@ +From 07b154fbc817e93f58c597644570a633c38d1c72 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Jan 2021 12:51:02 +0100 +Subject: [PATCH] ci: PowerTools repo was renamed to powertools in RHEL 8.3 + +See: https://wiki.centos.org/Manuals/ReleaseNotes/CentOS8.2011#Yum_repo_file_and_repoid_changes + +rhel-only +Related: #1871827 +--- + ci/travis-centos-rhel8.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index cd0857fd29..43e2cb2585 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -95,7 +95,7 @@ for phase in "${PHASES[@]}"; do + # Upgrade the container to get the most recent environment + $DOCKER_EXEC dnf -y upgrade + # Install systemd's build dependencies +- $DOCKER_EXEC dnf -q -y --enablerepo "PowerTools" builddep systemd ++ $DOCKER_EXEC dnf -q -y --enablerepo "powertools" builddep systemd + ;; + RUN) + info "Run phase" diff --git a/SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch b/SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch new file mode 100644 index 0000000..db85595 --- /dev/null +++ b/SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch @@ -0,0 +1,33 @@ +From 2dd82aad646bde5a0d49df8562e2578c8b3d04f4 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Jan 2021 13:00:33 +0100 +Subject: [PATCH] ci: use quay.io instead of Docker Hub to avoid rate limits + +Docker Hub introduced rate limits for anonymous users (100 requests per +six hours), which break our CI in the busier periods. Let's try to use +the quay.io CentOS image to mitigate this. + +rhel-only +Related: #1871827 +--- + ci/travis-centos-rhel8.sh | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index 43e2cb2585..ffe5813b1a 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -81,11 +81,11 @@ for phase in "${PHASES[@]}"; do + info "Setup phase" + info "Using Travis $CENTOS_RELEASE" + # Pull a Docker image and start a new container +- docker pull centos:$CENTOS_RELEASE ++ docker pull quay.io/centos/centos:$CENTOS_RELEASE + info "Starting container $CONT_NAME" + $DOCKER_RUN -v $REPO_ROOT:/build:rw \ + -w /build --privileged=true --name $CONT_NAME \ +- -dit --net=host centos:$CENTOS_RELEASE /sbin/init ++ -dit --net=host quay.io/centos/centos:$CENTOS_RELEASE /sbin/init + # Beautiful workaround for Fedora's version of Docker + sleep 1 + $DOCKER_EXEC dnf makecache diff --git a/SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch b/SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch new file mode 100644 index 0000000..70ba33f --- /dev/null +++ b/SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch @@ -0,0 +1,338 @@ +From 88ac207cc619935c64923e6f8fdef324a5b733d8 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Jan 2021 15:13:53 +0100 +Subject: [PATCH] ci: move jobs from Travis CI to GH Actions + +The OSS version of Travis CI is going to be merged with the commercial +one soon, essentially dropping the free tier, so let's move the CI jobs +to GitHub Actions to keep them up. + +rhel-only +Related: #1871827 +--- + .../workflows/unit_tests.sh | 28 +++---- + .github/workflows/unit_tests.yml | 28 +++++++ + .travis.yml | 48 ------------ + ci/travis-centos-rhel7.sh | 73 ------------------- + ci/travis_wait.bash | 61 ---------------- + 5 files changed, 37 insertions(+), 201 deletions(-) + rename ci/travis-centos-rhel8.sh => .github/workflows/unit_tests.sh (82%) + create mode 100644 .github/workflows/unit_tests.yml + delete mode 100644 .travis.yml + delete mode 100755 ci/travis-centos-rhel7.sh + delete mode 100644 ci/travis_wait.bash + +diff --git a/ci/travis-centos-rhel8.sh b/.github/workflows/unit_tests.sh +similarity index 82% +rename from ci/travis-centos-rhel8.sh +rename to .github/workflows/unit_tests.sh +index ffe5813b1a..ea4f7e7592 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/.github/workflows/unit_tests.sh +@@ -1,18 +1,9 @@ + #!/bin/bash + +-# Run this script from the root of the systemd's git repository +-# or set REPO_ROOT to a correct path. +-# +-# Example execution on Fedora: +-# dnf install docker +-# systemctl start docker +-# export CONT_NAME="my-fancy-container" +-# ci/travis-centos.sh SETUP RUN CLEANUP +- + PHASES=(${@:-SETUP RUN CLEANUP}) + CENTOS_RELEASE="${CENTOS_RELEASE:-latest}" + CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" +-DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" ++DOCKER_EXEC="${DOCKER_EXEC:-docker exec $CONT_NAME}" + DOCKER_RUN="${DOCKER_RUN:-docker run}" + REPO_ROOT="${REPO_ROOT:-$PWD}" + ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq diffutils) +@@ -71,9 +62,7 @@ function info() { + echo -e "\033[33;1m$1\033[0m" + } + +-set -e +- +-source "$(dirname $0)/travis_wait.bash" ++set -ex + + for phase in "${PHASES[@]}"; do + case $phase in +@@ -86,6 +75,7 @@ for phase in "${PHASES[@]}"; do + $DOCKER_RUN -v $REPO_ROOT:/build:rw \ + -w /build --privileged=true --name $CONT_NAME \ + -dit --net=host quay.io/centos/centos:$CENTOS_RELEASE /sbin/init ++ + # Beautiful workaround for Fedora's version of Docker + sleep 1 + $DOCKER_EXEC dnf makecache +@@ -97,10 +87,10 @@ for phase in "${PHASES[@]}"; do + # Install systemd's build dependencies + $DOCKER_EXEC dnf -q -y --enablerepo "powertools" builddep systemd + ;; +- RUN) ++ RUN|RUN_GCC) + info "Run phase" + # Build systemd +- docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}" ++ docker exec -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}" + $DOCKER_EXEC ninja -v -C build + # Let's install the new systemd and "reboot" the container to avoid + # unexpected fails due to incompatibilities with older systemd +@@ -108,16 +98,16 @@ for phase in "${PHASES[@]}"; do + docker restart $CONT_NAME + $DOCKER_EXEC ninja -C build test + ;; +- RUN_ASAN|RUN_CLANG_ASAN) ++ RUN_ASAN|RUN_GCC_ASAN|RUN_CLANG_ASAN) + if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then + ENV_VARS="-e CC=clang -e CXX=clang++" + MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764 + fi +- docker exec $ENV_VARS -it $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}" +- docker exec -it $CONT_NAME ninja -v -C build ++ docker exec $ENV_VARS $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}" ++ docker exec $CONT_NAME ninja -v -C build + + # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb. +- travis_wait docker exec --interactive=false \ ++ docker exec --interactive=false \ + -e UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \ + -e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \ + -e "TRAVIS=$TRAVIS" \ +diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml +new file mode 100644 +index 0000000000..15f5127a75 +--- /dev/null ++++ b/.github/workflows/unit_tests.yml +@@ -0,0 +1,28 @@ ++--- ++# vi: ts=2 sw=2 et: ++# ++name: Unit tests ++on: ++ pull_request: ++ branches: ++ - master ++ ++jobs: ++ build: ++ runs-on: ubuntu-20.04 ++ env: ++ CENTOS_RELEASE: "centos8" ++ CONT_NAME: "systemd-centos8-ci" ++ strategy: ++ fail-fast: false ++ matrix: ++ run_phase: [GCC, GCC_ASAN] ++ steps: ++ - name: Repository checkout ++ uses: actions/checkout@v1 ++ - name: Install build dependencies ++ run: sudo -E .github/workflows/unit_tests.sh SETUP ++ - name: Build & test (${{ matrix.run_phase }}) ++ run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }} ++ - name: Cleanup ++ run: sudo -E .github/workflows/unit_tests.sh CLEANUP +diff --git a/.travis.yml b/.travis.yml +deleted file mode 100644 +index 70c60cf24e..0000000000 +--- a/.travis.yml ++++ /dev/null +@@ -1,48 +0,0 @@ +-sudo: required +-dist: xenial +-services: +- - docker +- +-env: +- global: +- - CI_ROOT="$TRAVIS_BUILD_DIR/ci/" +- +-jobs: +- include: +- - name: CentOS 8 +- language: bash +- env: +- - CENTOS_RELEASE="centos8" +- - CONT_NAME="systemd-centos-$CENTOS_RELEASE" +- - DOCKER_EXEC="docker exec -ti $CONT_NAME" +- before_install: +- - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce +- - docker --version +- install: +- - $CI_ROOT/travis-centos-rhel8.sh SETUP +- script: +- - set -e +- # Build systemd +- - $CI_ROOT/travis-centos-rhel8.sh RUN +- - set +e +- after_script: +- - $CI_ROOT/travis-centos-rhel8.sh CLEANUP +- +- - name: CentOS 8 (ASan+UBSan) +- language: bash +- env: +- - CENTOS_RELEASE="centos8" +- - CONT_NAME="systemd-centos-$CENTOS_RELEASE" +- - DOCKER_EXEC="docker exec -ti $CONT_NAME" +- before_install: +- - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce +- - docker --version +- install: +- - $CI_ROOT/travis-centos-rhel8.sh SETUP +- script: +- - set -e +- # Build systemd +- - $CI_ROOT/travis-centos-rhel8.sh RUN_ASAN +- - set +e +- after_script: +- - $CI_ROOT/travis-centos-rhel8.sh CLEANUP +diff --git a/ci/travis-centos-rhel7.sh b/ci/travis-centos-rhel7.sh +deleted file mode 100755 +index 73416798ed..0000000000 +--- a/ci/travis-centos-rhel7.sh ++++ /dev/null +@@ -1,73 +0,0 @@ +-#!/bin/bash +- +-# Run this script from the root of the systemd's git repository +-# or set REPO_ROOT to a correct path. +-# +-# Example execution on Fedora: +-# dnf install docker +-# systemctl start docker +-# export CONT_NAME="my-fancy-container" +-# ci/travis-centos.sh SETUP RUN CLEANUP +- +-PHASES=(${@:-SETUP RUN CLEANUP}) +-CENTOS_RELEASE="${CENTOS_RELEASE:-latest}" +-CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" +-DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" +-DOCKER_RUN="${DOCKER_RUN:-docker run}" +-REPO_ROOT="${REPO_ROOT:-$PWD}" +-ADDITIONAL_DEPS=(yum-utils iputils hostname libasan libubsan clang llvm) +- +-function info() { +- echo -e "\033[33;1m$1\033[0m" +-} +- +-set -e +- +-source "$(dirname $0)/travis_wait.bash" +- +-for phase in "${PHASES[@]}"; do +- case $phase in +- SETUP) +- info "Setup phase" +- info "Using Travis $CENTOS_RELEASE" +- # Pull a Docker image and start a new container +- docker pull centos:$CENTOS_RELEASE +- info "Starting container $CONT_NAME" +- $DOCKER_RUN -v $REPO_ROOT:/build:rw \ +- -w /build --privileged=true --name $CONT_NAME \ +- -dit --net=host centos:$CENTOS_RELEASE /sbin/init +- # Beautiful workaround for Fedora's version of Docker +- sleep 1 +- $DOCKER_EXEC yum makecache +- # Install necessary build/test requirements +- $DOCKER_EXEC yum -y upgrade +- $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" +- $DOCKER_EXEC yum-builddep -y systemd +- ;; +- RUN) +- info "Run phase" +- # Build systemd +- $DOCKER_EXEC ./autogen.sh +- $DOCKER_EXEC ./configure --disable-timesyncd --disable-kdbus --disable-terminal \ +- --enable-gtk-doc --enable-compat-libs --disable-sysusers \ +- --disable-ldconfig --enable-lz4 --with-sysvinit-path=/etc/rc.d/init.d +- $DOCKER_EXEC make +- # Let's install the new systemd and "reboot" the container to avoid +- # unexpected fails due to incompatibilities with older systemd +- $DOCKER_EXEC make install +- docker restart $CONT_NAME +- if ! $DOCKER_EXEC make check; then +- $DOCKER_EXEC cat test-suite.log +- exit 1 +- fi +- ;; +- CLEANUP) +- info "Cleanup phase" +- docker stop $CONT_NAME +- docker rm -f $CONT_NAME +- ;; +- *) +- echo >&2 "Unknown phase '$phase'" +- exit 1 +- esac +-done +diff --git a/ci/travis_wait.bash b/ci/travis_wait.bash +deleted file mode 100644 +index acf6ad15e4..0000000000 +--- a/ci/travis_wait.bash ++++ /dev/null +@@ -1,61 +0,0 @@ +-# This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash +-# to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed +-# as soon as Travis CI has started to provide an easy way to export the functions to bash scripts. +- +-travis_jigger() { +- local cmd_pid="${1}" +- shift +- local timeout="${1}" +- shift +- local count=0 +- +- echo -e "\\n" +- +- while [[ "${count}" -lt "${timeout}" ]]; do +- count="$((count + 1))" +- echo -ne "Still running (${count} of ${timeout}): ${*}\\r" +- sleep 60 +- done +- +- echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n" +- kill -9 "${cmd_pid}" +-} +- +-travis_wait() { +- local timeout="${1}" +- +- if [[ "${timeout}" =~ ^[0-9]+$ ]]; then +- shift +- else +- timeout=20 +- fi +- +- local cmd=("${@}") +- local log_file="travis_wait_${$}.log" +- +- "${cmd[@]}" &>"${log_file}" & +- local cmd_pid="${!}" +- +- travis_jigger "${!}" "${timeout}" "${cmd[@]}" & +- local jigger_pid="${!}" +- local result +- +- { +- set +e +- wait "${cmd_pid}" 2>/dev/null +- result="${?}" +- ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}" +- set -e +- } +- +- if [[ "${result}" -eq 0 ]]; then +- echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" +- else +- echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" +- fi +- +- echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n" +- cat "${log_file}" +- +- return "${result}" +-} diff --git a/SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch b/SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch new file mode 100644 index 0000000..9f2143b --- /dev/null +++ b/SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch @@ -0,0 +1,31 @@ +From a11334f0eae67b5159a416193e2e37634281000a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 09:33:31 +0100 +Subject: [PATCH] unit: make UNIT() cast function deal with NULL pointers + +Fixes: #10681 +(cherry picked from commit bbf11206230d1b089118971f98a047151cb5c4fa) + +Related: #1871827 +--- + src/core/unit.h | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/core/unit.h b/src/core/unit.h +index 6e37fd6f5a..ec45b5fb48 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -597,7 +597,12 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; + } + + /* For casting the various unit types into a unit */ +-#define UNIT(u) (&(u)->meta) ++#define UNIT(u) \ ++ ({ \ ++ typeof(u) _u_ = (u); \ ++ Unit *_w_ = _u_ ? &(_u_)->meta : NULL; \ ++ _w_; \ ++ }) + + #define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0) + #define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0) diff --git a/SOURCES/0500-use-link-to-RHEL-8-docs.patch b/SOURCES/0500-use-link-to-RHEL-8-docs.patch new file mode 100644 index 0000000..ecc4968 --- /dev/null +++ b/SOURCES/0500-use-link-to-RHEL-8-docs.patch @@ -0,0 +1,25 @@ +From 6fb6c218fda0d5c3404049243b9392e9b0c7d537 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Fri, 11 Dec 2020 09:34:19 +0100 +Subject: [PATCH] use link to RHEL-8 docs + +RHEL-only + +Related: #1623116 +--- + man/systemctl.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index 56f94d084c..ed60a0739f 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -2005,7 +2005,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + For examples how to use systemctl in comparsion + with old service and chkconfig command please see: +- ++ + Managing System Services + + diff --git a/SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch b/SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch new file mode 100644 index 0000000..88ad896 --- /dev/null +++ b/SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch @@ -0,0 +1,37 @@ +From af9f03ba48dd75be8c6a923f70da9804b3a3a2c3 Mon Sep 17 00:00:00 2001 +From: Pavel Hrdina +Date: Wed, 25 Nov 2020 09:05:36 +0100 +Subject: [PATCH] cgroup: Also set blkio.bfq.weight + +Commit [1] added a workaround when unified cgroups are used but missed +legacy cgroups where there is the same issue. + +[1] + +Signed-off-by: Pavel Hrdina +(cherry picked from commit 35e7a62ca32a30169a94693b831e53c832251984) + +Resolves: #1657810 +--- + src/core/cgroup.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index f1ce070f9a..71e30fd4db 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1063,6 +1063,14 @@ static void cgroup_context_apply( + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight: %m"); + ++ /* FIXME: drop this when distro kernels properly support BFQ through "blkio.weight" ++ * See also: https://github.com/systemd/systemd/pull/13335 */ ++ xsprintf(buf, "%" PRIu64 "\n", weight); ++ r = cg_set_attribute("blkio", path, "blkio.bfq.weight", buf); ++ if (r < 0) ++ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, ++ "Failed to set blkio.bfq.weight: %m"); ++ + if (has_io) { + CGroupIODeviceWeight *w; + diff --git a/SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch b/SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch new file mode 100644 index 0000000..3fdb46f --- /dev/null +++ b/SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch @@ -0,0 +1,37 @@ +From ea425381a675a2ce4d9519d534fe27c1012ac92e Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Mon, 28 Jan 2019 12:07:37 +0100 +Subject: [PATCH] units: make sure initrd-cleanup.service terminates before + switching to rootfs + +A follow-up for commit a8cb1dc3e0fa81aff. + +Commit a8cb1dc3e0fa81aff made sure that initrd-cleanup.service won't be stopped +when initrd-switch-root.target is isolated. + +However even with this change, it might happen that initrd-cleanup.service +survives the switch to rootfs (since it has no ordering constraints against +initrd-switch-root.target) and is stopped right after when default.target is +isolated. This led to initrd-cleanup.service entering in failed state as it +happens when oneshot services are stopped. + +This patch along with a8cb1dc3e0fa81aff should fix issue #4343. + +Fixes: #4343 +(cherry picked from commit e2c7c94ea35fe7e669afb51bfc2251158b522ea5) + +Related: #1657810 +--- + units/initrd-switch-root.target | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/initrd-switch-root.target b/units/initrd-switch-root.target +index ad82245121..ea4f02618f 100644 +--- a/units/initrd-switch-root.target ++++ b/units/initrd-switch-root.target +@@ -15,4 +15,4 @@ Requires=initrd-switch-root.service + Before=initrd-switch-root.service + AllowIsolate=yes + Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service +-After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target ++After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target initrd-cleanup.service diff --git a/SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch b/SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch new file mode 100644 index 0000000..5e5d3d4 --- /dev/null +++ b/SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch @@ -0,0 +1,73 @@ +From c67be1c7d69a0662ab85720aa0209110c39479f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Wed, 27 Nov 2019 19:43:47 +0100 +Subject: [PATCH] core: reload SELinux label cache on daemon-reload + +Reloading the SELinux label cache here enables a light-wight follow-up of a SELinux policy change, e.g. adding a label for a RuntimeDirectory. + +Closes: #13363 +(cherry picked from commit a9dfac21ec850eb5dcaf1ae9ef729389e4c12802) + +Resolves: #1888912 +--- + src/basic/selinux-util.c | 20 ++++++++++++++++++++ + src/basic/selinux-util.h | 1 + + src/core/main.c | 2 ++ + 3 files changed, 23 insertions(+) + +diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c +index e15bd7e1fa..f69d88eb1e 100644 +--- a/src/basic/selinux-util.c ++++ b/src/basic/selinux-util.c +@@ -105,6 +105,26 @@ void mac_selinux_finish(void) { + #endif + } + ++void mac_selinux_reload(void) { ++ ++#if HAVE_SELINUX ++ struct selabel_handle *backup_label_hnd; ++ ++ if (!label_hnd) ++ return; ++ ++ backup_label_hnd = TAKE_PTR(label_hnd); ++ ++ /* try to initialize new handle ++ * on success close backup ++ * on failure restore backup */ ++ if (mac_selinux_init() == 0) ++ selabel_close(backup_label_hnd); ++ else ++ label_hnd = backup_label_hnd; ++#endif ++} ++ + int mac_selinux_fix(const char *path, LabelFixFlags flags) { + + #if HAVE_SELINUX +diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h +index 08314057fb..abcfabe777 100644 +--- a/src/basic/selinux-util.h ++++ b/src/basic/selinux-util.h +@@ -13,6 +13,7 @@ void mac_selinux_retest(void); + + int mac_selinux_init(void); + void mac_selinux_finish(void); ++void mac_selinux_reload(void); + + int mac_selinux_fix(const char *path, LabelFixFlags flags); + int mac_selinux_apply(const char *path, const char *label); +diff --git a/src/core/main.c b/src/core/main.c +index d897155644..d5c41da0c4 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1682,6 +1682,8 @@ static int invoke_main_loop( + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + ++ mac_selinux_reload(); ++ + (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); + + set_manager_defaults(m); diff --git a/SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch b/SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch new file mode 100644 index 0000000..4b51123 --- /dev/null +++ b/SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch @@ -0,0 +1,140 @@ +From 4f4e8bbd9ad46fc146a36f52790bc4920f42ef1f Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Mon, 2 Jul 2018 10:22:56 +0200 +Subject: [PATCH] selinux: introduce mac_selinux_create_file_prepare_at() + +(cherry picked from commit 7e531a5265687aef5177b070c36ca4ceab42e768) + +Related: #1888912 +--- + src/basic/selinux-util.c | 83 ++++++++++++++++++++++++++++++---------- + src/basic/selinux-util.h | 1 + + 2 files changed, 63 insertions(+), 21 deletions(-) + +diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c +index f69d88eb1e..a078ce23ef 100644 +--- a/src/basic/selinux-util.c ++++ b/src/basic/selinux-util.c +@@ -336,48 +336,89 @@ char* mac_selinux_free(char *label) { + return NULL; + } + +-int mac_selinux_create_file_prepare(const char *path, mode_t mode) { +- + #if HAVE_SELINUX ++static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) { + _cleanup_freecon_ char *filecon = NULL; ++ _cleanup_free_ char *path = NULL; + int r; + +- assert(path); +- +- if (!label_hnd) +- return 0; +- +- if (path_is_absolute(path)) +- r = selabel_lookup_raw(label_hnd, &filecon, path, mode); +- else { +- _cleanup_free_ char *newpath = NULL; +- +- r = path_make_absolute_cwd(path, &newpath); +- if (r < 0) +- return r; +- +- r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); +- } ++ assert(abspath); ++ assert(path_is_absolute(abspath)); + ++ r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); + if (r < 0) { + /* No context specified by the policy? Proceed without setting it. */ + if (errno == ENOENT) + return 0; + +- log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path); ++ log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath); + } else { + if (setfscreatecon_raw(filecon) >= 0) + return 0; /* Success! */ + +- log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path); ++ log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath); + } + + if (security_getenforce() > 0) + return -errno; + +-#endif + return 0; + } ++#endif ++ ++int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) { ++ int r = 0; ++ ++#if HAVE_SELINUX ++ _cleanup_free_ char *abspath = NULL; ++ _cleanup_close_ int fd = -1; ++ ++ assert(path); ++ ++ if (!label_hnd) ++ return 0; ++ ++ if (!path_is_absolute(path)) { ++ _cleanup_free_ char *p = NULL; ++ ++ if (dirfd == AT_FDCWD) ++ r = safe_getcwd(&p); ++ else ++ r = fd_get_path(dirfd, &p); ++ if (r < 0) ++ return r; ++ ++ abspath = path_join(NULL, p, path); ++ if (!abspath) ++ return -ENOMEM; ++ ++ path = abspath; ++ } ++ ++ r = selinux_create_file_prepare_abspath(path, mode); ++#endif ++ return r; ++} ++ ++int mac_selinux_create_file_prepare(const char *path, mode_t mode) { ++ int r = 0; ++ ++#if HAVE_SELINUX ++ _cleanup_free_ char *abspath = NULL; ++ ++ assert(path); ++ ++ if (!label_hnd) ++ return 0; ++ ++ r = path_make_absolute_cwd(path, &abspath); ++ if (r < 0) ++ return r; ++ ++ r = selinux_create_file_prepare_abspath(abspath, mode); ++#endif ++ return r; ++} + + void mac_selinux_create_file_clear(void) { + +diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h +index abcfabe777..639c35b687 100644 +--- a/src/basic/selinux-util.h ++++ b/src/basic/selinux-util.h +@@ -24,6 +24,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * + char* mac_selinux_free(char *label); + + int mac_selinux_create_file_prepare(const char *path, mode_t mode); ++int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode); + void mac_selinux_create_file_clear(void); + + int mac_selinux_create_socket_prepare(const char *label); diff --git a/SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch b/SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch new file mode 100644 index 0000000..4bba960 --- /dev/null +++ b/SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch @@ -0,0 +1,135 @@ +From 4e48673172b012a06575e4f5b681d3554eded2e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Mon, 6 Jan 2020 15:27:23 +0100 +Subject: [PATCH] selinux: add trigger for policy reload to refresh internal + selabel cache + +Reload the internal selabel cache automatically on SELinux policy reloads so non pid-1 daemons are participating. + +Run the reload function `mac_selinux_reload()` not manually on daemon-reload, but rather pass it as callback to libselinux. +Trigger the callback prior usage of the systemd internal selabel cache by depleting the selinux netlink socket via `avc_netlink_check_nb()`. + +Improves: a9dfac21ec85 ("core: reload SELinux label cache on daemon-reload") +Improves: #13363 +(cherry picked from commit 61f3e897f13101f29fb8027e8839498a469ad58e) + +Related: #1888912 +--- + src/basic/selinux-util.c | 23 +++++++++++++++++++---- + src/basic/selinux-util.h | 1 - + src/core/main.c | 2 -- + 3 files changed, 19 insertions(+), 7 deletions(-) + +diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c +index a078ce23ef..bfe3d015aa 100644 +--- a/src/basic/selinux-util.c ++++ b/src/basic/selinux-util.c +@@ -10,6 +10,7 @@ + #include + + #if HAVE_SELINUX ++#include + #include + #include + #include +@@ -32,6 +33,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); + #define _cleanup_freecon_ _cleanup_(freeconp) + #define _cleanup_context_free_ _cleanup_(context_freep) + ++static int mac_selinux_reload(int seqno); ++ + static int cached_use = -1; + static struct selabel_handle *label_hnd = NULL; + +@@ -63,6 +66,8 @@ int mac_selinux_init(void) { + usec_t before_timestamp, after_timestamp; + struct mallinfo before_mallinfo, after_mallinfo; + ++ selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload); ++ + if (label_hnd) + return 0; + +@@ -105,13 +110,12 @@ void mac_selinux_finish(void) { + #endif + } + +-void mac_selinux_reload(void) { +- + #if HAVE_SELINUX ++static int mac_selinux_reload(int seqno) { + struct selabel_handle *backup_label_hnd; + + if (!label_hnd) +- return; ++ return 0; + + backup_label_hnd = TAKE_PTR(label_hnd); + +@@ -122,8 +126,10 @@ void mac_selinux_reload(void) { + selabel_close(backup_label_hnd); + else + label_hnd = backup_label_hnd; +-#endif ++ ++ return 0; + } ++#endif + + int mac_selinux_fix(const char *path, LabelFixFlags flags) { + +@@ -152,6 +158,9 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) { + if (fstat(fd, &st) < 0) + return -errno; + ++ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ ++ (void) avc_netlink_check_nb(); ++ + if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) { + r = -errno; + +@@ -345,6 +354,9 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) + assert(abspath); + assert(path_is_absolute(abspath)); + ++ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ ++ (void) avc_netlink_check_nb(); ++ + r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); + if (r < 0) { + /* No context specified by the policy? Proceed without setting it. */ +@@ -496,6 +508,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { + + path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); + ++ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ ++ (void) avc_netlink_check_nb(); ++ + if (path_is_absolute(path)) + r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); + else { +diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h +index 639c35b687..bd5207c318 100644 +--- a/src/basic/selinux-util.h ++++ b/src/basic/selinux-util.h +@@ -13,7 +13,6 @@ void mac_selinux_retest(void); + + int mac_selinux_init(void); + void mac_selinux_finish(void); +-void mac_selinux_reload(void); + + int mac_selinux_fix(const char *path, LabelFixFlags flags); + int mac_selinux_apply(const char *path, const char *label); +diff --git a/src/core/main.c b/src/core/main.c +index d5c41da0c4..d897155644 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1682,8 +1682,6 @@ static int invoke_main_loop( + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + +- mac_selinux_reload(); +- + (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); + + set_manager_defaults(m); diff --git a/SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch b/SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch new file mode 100644 index 0000000..0ce9150 --- /dev/null +++ b/SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch @@ -0,0 +1,24 @@ +From fb58a56c6c1c2749ba634abd9ad76f4e718269a1 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 5 Jan 2021 12:30:15 +0100 +Subject: [PATCH] udev/net_id: give RHEL-8.4 naming scheme a name + +Follow-up for bb6114af097da0cd9c5081e42db718559130687f + +Related: #1827462 +--- + src/udev/udev-builtin-net_id.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index d8c56b62bb..7c153f0aef 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -150,6 +150,7 @@ static const NamingScheme naming_schemes[] = { + { "rhel-8.1", NAMING_RHEL_8_1 }, + { "rhel-8.2", NAMING_RHEL_8_2 }, + { "rhel-8.3", NAMING_RHEL_8_3 }, ++ { "rhel-8.4", NAMING_RHEL_8_4 }, + /* … add more schemes here, as the logic to name devices is updated … */ + }; + diff --git a/SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch b/SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch new file mode 100644 index 0000000..e9ac657 --- /dev/null +++ b/SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch @@ -0,0 +1,63 @@ +From 29c5b8dd6228c4401f034ca0aa85f99ac42cf8dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 5 Nov 2020 17:55:25 +0100 +Subject: [PATCH] basic/stat-util: make mtime check stricter and use entire + timestamp + +Note that st_mtime member of struct stat is defined as follows, + + #define st_mtime st_mtim.tv_sec + +Hence we omitted checking nanosecond part of the timestamp (struct +timespec) and possibly would miss modifications that happened within the +same second. + +(cherry picked from commit a59b0a9f768f6e27b25f4f1bab6de08842e78d74) + +Related: #1642728 +--- + src/basic/stat-util.c | 22 ++++++++++++++++++++++ + src/basic/stat-util.h | 2 ++ + 2 files changed, 24 insertions(+) + +diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c +index 26aee9bad6..c61c4c0517 100644 +--- a/src/basic/stat-util.c ++++ b/src/basic/stat-util.c +@@ -287,3 +287,25 @@ int fd_verify_regular(int fd) { + + return stat_verify_regular(&st); + } ++ ++bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { ++ ++ /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to ++ * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file ++ * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file ++ * size, backing device, inode type and if this refers to a device not the major/minor. ++ * ++ * Note that we don't care if file attributes such as ownership or access mode change, this here is ++ * about contents of the file. The purpose here is to detect file contents changes, and nothing ++ * else. */ ++ ++ return a && b && ++ (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ ++ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ ++ a->st_mtim.tv_sec == b->st_mtim.tv_sec && ++ a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && ++ (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ ++ a->st_dev == b->st_dev && ++ a->st_ino == b->st_ino && ++ (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ ++} +\ No newline at end of file +diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h +index f8014ed30b..9e1a2b70da 100644 +--- a/src/basic/stat-util.h ++++ b/src/basic/stat-util.h +@@ -58,3 +58,5 @@ int path_is_temporary_fs(const char *path); + + int stat_verify_regular(const struct stat *st); + int fd_verify_regular(int fd); ++ ++bool stat_inode_unmodified(const struct stat *a, const struct stat *b); diff --git a/SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch b/SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch new file mode 100644 index 0000000..e20137b --- /dev/null +++ b/SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch @@ -0,0 +1,457 @@ +From 1d5f966c1758eb620755fcae54abd07a1ac36d3d Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 6 Jan 2021 11:43:50 +0100 +Subject: [PATCH] udev: make algorithm that selects highest priority devlink + less susceptible to race conditions + +Previously it was very likely, when multiple contenders for the symlink +appear in parallel, that algorithm would select wrong symlink (i.e. one +with lower-priority). + +Now the algorithm is much more defensive and when we detect change in +set of contenders for the symlink we reevaluate the selection. Same +happens when new symlink replaces already existing symlink that points +to different device node. + +Resolves: #1642728 +--- + src/udev/udev-event.c | 71 +++++++----- + src/udev/udev-node.c | 244 ++++++++++++++++++++++++++++++------------ + 2 files changed, 216 insertions(+), 99 deletions(-) + +diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c +index fd8406d959..9004634f65 100644 +--- a/src/udev/udev-event.c ++++ b/src/udev/udev-event.c +@@ -833,6 +833,41 @@ static int rename_netif(struct udev_event *event) { + return 0; + } + ++static void update_devnode(struct udev_event *event) { ++ struct udev_device *dev = event->dev; ++ ++ if (major(udev_device_get_devnum(dev)) > 0) { ++ bool apply; ++ ++ /* remove/update possible left-over symlinks from old database entry */ ++ if (event->dev_db != NULL) ++ udev_node_update_old_links(dev, event->dev_db); ++ ++ if (!event->owner_set) ++ event->uid = udev_device_get_devnode_uid(dev); ++ ++ if (!event->group_set) ++ event->gid = udev_device_get_devnode_gid(dev); ++ ++ if (!event->mode_set) { ++ if (udev_device_get_devnode_mode(dev) > 0) { ++ /* kernel supplied value */ ++ event->mode = udev_device_get_devnode_mode(dev); ++ } else if (event->gid > 0) { ++ /* default 0660 if a group is assigned */ ++ event->mode = 0660; ++ } ++ else { ++ /* default 0600 */ ++ event->mode = 0600; ++ } ++ } ++ ++ apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; ++ udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); ++ } ++} ++ + void udev_event_execute_rules(struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, +@@ -891,35 +926,7 @@ void udev_event_execute_rules(struct udev_event *event, + } + } + +- if (major(udev_device_get_devnum(dev)) > 0) { +- bool apply; +- +- /* remove/update possible left-over symlinks from old database entry */ +- if (event->dev_db != NULL) +- udev_node_update_old_links(dev, event->dev_db); +- +- if (!event->owner_set) +- event->uid = udev_device_get_devnode_uid(dev); +- +- if (!event->group_set) +- event->gid = udev_device_get_devnode_gid(dev); +- +- if (!event->mode_set) { +- if (udev_device_get_devnode_mode(dev) > 0) { +- /* kernel supplied value */ +- event->mode = udev_device_get_devnode_mode(dev); +- } else if (event->gid > 0) { +- /* default 0660 if a group is assigned */ +- event->mode = 0660; +- } else { +- /* default 0600 */ +- event->mode = 0600; +- } +- } +- +- apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; +- udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); +- } ++ update_devnode(event); + + /* preserve old, or get new initialization timestamp */ + udev_device_ensure_usec_initialized(event->dev, event->dev_db); +@@ -927,6 +934,12 @@ void udev_event_execute_rules(struct udev_event *event, + /* (re)write database file */ + udev_device_tag_index(dev, event->dev_db, true); + udev_device_update_db(dev); ++ ++ /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database, ++ * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure ++ * symlinks point to devices that claim them with the highest priority. */ ++ update_devnode(event); ++ + udev_device_set_is_initialized(dev); + + event->dev_db = udev_device_unref(event->dev_db); +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index 333dcae6b9..2eeeccdd3a 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -13,19 +13,27 @@ + #include + + #include "device-nodes.h" ++#include "device-private.h" + #include "dirent-util.h" ++#include "fd-util.h" + #include "format-util.h" + #include "fs-util.h" ++#include "sd-device.h" + #include "selinux-util.h" + #include "smack-util.h" ++#include "stat-util.h" + #include "stdio-util.h" + #include "string-util.h" + #include "udev.h" ++#include "libudev-device-internal.h" + +-static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { ++#define LINK_UPDATE_MAX_RETRIES 128 ++ ++static int node_symlink(sd_device *dev, const char *node, const char *slink) { + struct stat stats; + char target[UTIL_PATH_SIZE]; + char *s; ++ const char *id_filename; + size_t l; + char slink_tmp[UTIL_PATH_SIZE + 32]; + int i = 0; +@@ -89,7 +97,10 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s + } + + log_debug("atomically replace '%s'", slink); +- strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); ++ err = device_get_id_filename(dev, &id_filename); ++ if (err < 0) ++ return log_error_errno(err, "Failed to get id_filename: %m"); ++ strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", id_filename, NULL); + unlink(slink_tmp); + do { + err = mkdir_parents_label(slink_tmp, 0755); +@@ -109,104 +120,187 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s + if (err != 0) { + log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); + unlink(slink_tmp); +- } ++ } else ++ /* Tell caller that we replaced already existing symlink. */ ++ return 1; + exit: + return err; + } + + /* find device node of device with highest priority */ +-static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) { +- struct udev *udev = udev_device_get_udev(dev); +- DIR *dir; ++static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) { ++ _cleanup_closedir_ DIR *dir = NULL; ++ _cleanup_free_ char *target = NULL; + struct dirent *dent; +- int priority = 0; +- const char *target = NULL; ++ int r, priority = 0; ++ ++ assert(!add || dev); ++ assert(stackdir); ++ assert(ret); + + if (add) { +- priority = udev_device_get_devlink_priority(dev); +- strscpy(buf, bufsize, udev_device_get_devnode(dev)); +- target = buf; ++ const char *devnode; ++ ++ r = device_get_devlink_priority(dev, &priority); ++ if (r < 0) ++ return r; ++ ++ r = sd_device_get_devname(dev, &devnode); ++ if (r < 0) ++ return r; ++ ++ target = strdup(devnode); ++ if (!target) ++ return -ENOMEM; + } + + dir = opendir(stackdir); +- if (dir == NULL) +- return target; ++ if (!dir) { ++ if (target) { ++ *ret = TAKE_PTR(target); ++ return 0; ++ } ++ ++ return -errno; ++ } ++ + FOREACH_DIRENT_ALL(dent, dir, break) { +- struct udev_device *dev_db; ++ _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL; ++ const char *devnode, *id_filename; ++ int db_prio = 0; + + if (dent->d_name[0] == '\0') + break; + if (dent->d_name[0] == '.') + continue; + +- log_debug("found '%s' claiming '%s'", dent->d_name, stackdir); ++ log_debug("Found '%s' claiming '%s'", dent->d_name, stackdir); ++ ++ if (device_get_id_filename(dev, &id_filename) < 0) ++ continue; + + /* did we find ourself? */ +- if (streq(dent->d_name, udev_device_get_id_filename(dev))) ++ if (streq(dent->d_name, id_filename)) + continue; + +- dev_db = udev_device_new_from_device_id(udev, dent->d_name); +- if (dev_db != NULL) { +- const char *devnode; +- +- devnode = udev_device_get_devnode(dev_db); +- if (devnode != NULL) { +- if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { +- log_debug("'%s' claims priority %i for '%s'", +- udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); +- priority = udev_device_get_devlink_priority(dev_db); +- strscpy(buf, bufsize, devnode); +- target = buf; +- } +- } +- udev_device_unref(dev_db); ++ if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0) ++ continue; ++ ++ if (sd_device_get_devname(dev_db, &devnode) < 0) ++ continue; ++ ++ if (device_get_devlink_priority(dev_db, &db_prio) < 0) ++ continue; ++ ++ if (target && db_prio <= priority) ++ continue; ++ ++ if (DEBUG_LOGGING) { ++ const char *syspath = NULL; ++ ++ (void) sd_device_get_syspath(dev_db, &syspath); ++ log_debug("Device '%s' claims priority %i for '%s'", strnull(syspath), db_prio, stackdir); + } ++ ++ r = free_and_strdup(&target, devnode); ++ if (r < 0) ++ return r; ++ priority = db_prio; + } +- closedir(dir); +- return target; ++ ++ if (!target) ++ return -ENOENT; ++ ++ *ret = TAKE_PTR(target); ++ return 0; + } + ++ + /* manage "stack of names" with possibly specified device priorities */ +-static void link_update(struct udev_device *dev, const char *slink, bool add) { +- char name_enc[UTIL_PATH_SIZE]; +- char filename[UTIL_PATH_SIZE * 2]; +- char dirname[UTIL_PATH_SIZE]; +- const char *target; +- char buf[UTIL_PATH_SIZE]; ++static int link_update(sd_device *dev, const char *slink, bool add) { ++ _cleanup_free_ char *filename = NULL, *dirname = NULL; ++ char name_enc[PATH_MAX]; ++ const char *id_filename; ++ int i, r, retries; ++ ++ assert(dev); ++ assert(slink); ++ ++ r = device_get_id_filename(dev, &id_filename); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to get id_filename: %m"); + + util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc)); +- strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); +- strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); ++ dirname = path_join(NULL, "/run/udev/links/", name_enc); ++ if (!dirname) ++ return log_oom(); ++ filename = path_join(NULL, dirname, id_filename); ++ if (!filename) ++ return log_oom(); ++ ++ if (!add) { ++ if (unlink(filename) == 0) ++ (void) rmdir(dirname); ++ } else ++ for (;;) { ++ _cleanup_close_ int fd = -1; ++ ++ r = mkdir_parents(filename, 0755); ++ if (!IN_SET(r, 0, -ENOENT)) ++ return r; + +- if (!add && unlink(filename) == 0) +- rmdir(dirname); ++ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); ++ if (fd >= 0) ++ break; ++ if (errno != ENOENT) ++ return -errno; ++ } + +- target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); +- if (target == NULL) { +- log_debug("no reference left, remove '%s'", slink); +- if (unlink(slink) == 0) +- rmdir_parents(slink, "/"); +- } else { +- log_debug("creating link '%s' to '%s'", slink, target); +- node_symlink(dev, target, slink); +- } ++ /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink ++ * will be fixed in the second invocation. */ ++ (void) sd_device_get_is_initialized(dev, &r); ++ retries = r > 0 ? LINK_UPDATE_MAX_RETRIES : 1; + +- if (add) { +- int err; ++ for (i = 0; i < retries; i++) { ++ _cleanup_free_ char *target = NULL; ++ struct stat st1 = {}, st2 = {}; + +- do { +- int fd; ++ r = stat(dirname, &st1); ++ if (r < 0 && errno != ENOENT) ++ return -errno; + +- err = mkdir_parents(filename, 0755); +- if (!IN_SET(err, 0, -ENOENT)) ++ r = link_find_prioritized(dev, add, dirname, &target); ++ if (r == -ENOENT) { ++ log_debug("No reference left, removing '%s'", slink); ++ if (unlink(slink) == 0) ++ (void) rmdir_parents(slink, "/"); ++ ++ break; ++ } else if (r < 0) ++ return log_error_errno(r, "Failed to determine highest priority symlink: %m"); ++ ++ r = node_symlink(dev, target, slink); ++ if (r < 0) { ++ (void) unlink(filename); ++ break; ++ } else if (r == 1) ++ /* We have replaced already existing symlink, possibly there is some other device trying ++ * to claim the same symlink. Let's do one more iteration to give us a chance to fix ++ * the error if other device actually claims the symlink with higher priority. */ ++ continue; ++ ++ /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */ ++ if ((st1.st_mode & S_IFMT) != 0) { ++ r = stat(dirname, &st2); ++ if (r < 0 && errno != ENOENT) ++ return -errno; ++ ++ if (stat_inode_unmodified(&st1, &st2)) + break; +- fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); +- if (fd >= 0) +- close(fd); +- else +- err = -errno; +- } while (err == -ENOENT); ++ } + } ++ ++ return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; + } + + void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { +@@ -233,7 +327,7 @@ void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev + + log_debug("update old name, '%s' no longer belonging to '%s'", + name, udev_device_get_devpath(dev)); +- link_update(dev, name, false); ++ link_update(dev->device, name, false); + } + } + +@@ -338,11 +432,16 @@ void udev_node_add(struct udev_device *dev, bool apply, + xsprintf_dev_num_path(filename, + streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", + udev_device_get_devnum(dev)); +- node_symlink(dev, udev_device_get_devnode(dev), filename); ++ node_symlink(dev->device, udev_device_get_devnode(dev), filename); + + /* create/update symlinks, add symlinks to name index */ +- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) +- link_update(dev, udev_list_entry_get_name(list_entry), true); ++ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { ++ int r; ++ ++ r = link_update(dev->device, udev_list_entry_get_name(list_entry), true); ++ if (r < 0) ++ log_info_errno(r, "Failed to update device symlinks: %m"); ++ } + } + + void udev_node_remove(struct udev_device *dev) { +@@ -350,8 +449,13 @@ void udev_node_remove(struct udev_device *dev) { + char filename[DEV_NUM_PATH_MAX]; + + /* remove/update symlinks, remove symlinks from name index */ +- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) +- link_update(dev, udev_list_entry_get_name(list_entry), false); ++ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { ++ int r; ++ ++ r = link_update(dev->device, udev_list_entry_get_name(list_entry), false); ++ if (r < 0) ++ log_info_errno(r, "Failed to update device symlinks: %m"); ++ } + + /* remove /dev/{block,char}/$major:$minor */ + xsprintf_dev_num_path(filename, diff --git a/SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch b/SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch new file mode 100644 index 0000000..4dccdbc --- /dev/null +++ b/SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch @@ -0,0 +1,32 @@ +From 6a908a38135d050b7c271fdea9c061d7e7ad8ef7 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 23 Oct 2018 07:23:01 +0900 +Subject: [PATCH] test: create /dev/null in test-udev.pl + +(cherry picked from commit a41ff38b0999fb83464309a29b8f39450b8d4b85) + +Related: #1642728 +--- + test/udev-test.pl | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 0433629c7c..a1c24f49b4 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1537,13 +1537,14 @@ sub udev_setup { + system("umount", $udev_tmpfs); + rmdir($udev_tmpfs); + mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; +- system("mount", "-o", "rw,mode=755,nosuid,noexec,nodev", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; ++ system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; + + mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n"; + # setting group and mode of udev_dev ensures the tests work + # even if the parent directory has setgid bit enabled. + chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; + chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; ++ system("mknod", $udev_dev . "/null", "c", "1", "3") && "unable to create $udev_dev/null"; + + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + diff --git a/SOURCES/0510-test-missing-die.patch b/SOURCES/0510-test-missing-die.patch new file mode 100644 index 0000000..959a689 --- /dev/null +++ b/SOURCES/0510-test-missing-die.patch @@ -0,0 +1,27 @@ +From 70bf708d5360372aa541e25ff512609230781dd6 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 7 Nov 2018 14:56:20 +0900 +Subject: [PATCH] test: missing "die" + +Follow-up for a41ff38b0999fb83464309a29b8f39450b8d4b85. + +(cherry picked from commit 11d93952ea806de2b6e9fb381153115cccc7b5e8) + +Related: #1642728 +--- + test/udev-test.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a1c24f49b4..61bd3d703a 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1544,7 +1544,7 @@ sub udev_setup { + # even if the parent directory has setgid bit enabled. + chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; + chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; +- system("mknod", $udev_dev . "/null", "c", "1", "3") && "unable to create $udev_dev/null"; ++ system("mknod", $udev_dev . "/null", "c", "1", "3") && die "unable to create $udev_dev/null"; + + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + diff --git a/SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch b/SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch new file mode 100644 index 0000000..eeadbda --- /dev/null +++ b/SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch @@ -0,0 +1,33 @@ +From 1b133f2ca15f0a15b05407b2c04521d7de88dfa2 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 9 Nov 2018 03:14:04 +0100 +Subject: [PATCH] udev-test: remove a check for whether the test is run in a + container + +It's too broad a check that prevents the test from running on Travis CI. + +(cherry picked from commit 881886ef08d50951159633248b0f73977c5d6924) + +Related: #1642728 +--- + test/udev-test.pl | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 61bd3d703a..05b3e17188 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1646,13 +1646,6 @@ if ($? >> 8 == 0) { + exit($EXIT_TEST_SKIP); + } + +-# skip the test when running in a container +-system("systemd-detect-virt", "-c", "-q"); +-if ($? >> 8 == 0) { +- print "Running in a container, skipping the test.\n"; +- exit($EXIT_TEST_SKIP); +-} +- + udev_setup(); + + my $test_num = 1; diff --git a/SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch b/SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch new file mode 100644 index 0000000..37970ab --- /dev/null +++ b/SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch @@ -0,0 +1,94 @@ +From 8c82f3a4aa2d029dcc303cbf95a71194aa5ac9c3 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 9 Nov 2018 04:01:15 +0100 +Subject: [PATCH] udev-test: skip the test only if it can't setup its + environment + +This is basically a replacement for 0eb3cc88504b5d8f74. + +(cherry picked from commit 110a13202eab6d92678abcde08372d4afac1cc45) + +Related: #1642728 +--- + src/test/test-udev.c | 8 ++++++++ + test/udev-test.pl | 24 +++++++++++++++++++++--- + 2 files changed, 29 insertions(+), 3 deletions(-) + +diff --git a/src/test/test-udev.c b/src/test/test-udev.c +index bed51c1270..f098fab721 100644 +--- a/src/test/test-udev.c ++++ b/src/test/test-udev.c +@@ -65,6 +65,11 @@ int main(int argc, char *argv[]) { + log_parse_environment(); + log_open(); + ++ if (!IN_SET(argc, 2, 3)) { ++ log_error("This program needs one or two arguments, %d given", argc - 1); ++ return EXIT_FAILURE; ++ } ++ + err = fake_filesystems(); + if (err < 0) + return EXIT_FAILURE; +@@ -73,6 +78,9 @@ int main(int argc, char *argv[]) { + if (udev == NULL) + return EXIT_FAILURE; + ++ if (argc == 2) ++ return EXIT_SUCCESS; ++ + log_debug("version %s", PACKAGE_VERSION); + mac_selinux_init(); + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 05b3e17188..aa38bae0b1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1537,18 +1537,28 @@ sub udev_setup { + system("umount", $udev_tmpfs); + rmdir($udev_tmpfs); + mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; +- system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; ++ ++ if (system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) { ++ warn "unable to mount tmpfs"; ++ return 0; ++ } + + mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n"; + # setting group and mode of udev_dev ensures the tests work + # even if the parent directory has setgid bit enabled. + chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; + chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; +- system("mknod", $udev_dev . "/null", "c", "1", "3") && die "unable to create $udev_dev/null"; ++ ++ if (system("mknod", $udev_dev . "/null", "c", "1", "3")) { ++ warn "unable to create $udev_dev/null"; ++ return 0; ++ } + + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + + system("rm", "-rf", "$udev_run"); ++ ++ return 1; + } + + sub run_test { +@@ -1646,7 +1656,15 @@ if ($? >> 8 == 0) { + exit($EXIT_TEST_SKIP); + } + +-udev_setup(); ++if (!udev_setup()) { ++ warn "Failed to set up the environment, skipping the test"; ++ exit($EXIT_TEST_SKIP); ++} ++ ++if (!system($udev_bin, "check")) { ++ warn "$udev_bin failed to set up the environment, skipping the test"; ++ exit($EXIT_TEST_SKIP); ++} + + my $test_num = 1; + my @list; diff --git a/SOURCES/0513-udev-test-fix-test-skip-condition.patch b/SOURCES/0513-udev-test-fix-test-skip-condition.patch new file mode 100644 index 0000000..165aadb --- /dev/null +++ b/SOURCES/0513-udev-test-fix-test-skip-condition.patch @@ -0,0 +1,33 @@ +From f44fcdde656036f0388fc8244b8960c1873a3a08 Mon Sep 17 00:00:00 2001 +From: Alexey Bogdanenko +Date: Sat, 8 Dec 2018 11:02:30 +0300 +Subject: [PATCH] udev-test: fix test skip condition + +When there is a failure to setup the environment, the following happens: + +1. Command "./test-udev check" exits with non-zero code. +2. Perl function "system" returns the code. +3. The code is evaluated as true by Perl. + +Then we stop the test. + +(cherry picked from commit 7935dae547caf164d807237f1009a9e9fa510337) + +Related: #1642728 +--- + test/udev-test.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index aa38bae0b1..3517feab15 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1661,7 +1661,7 @@ if (!udev_setup()) { + exit($EXIT_TEST_SKIP); + } + +-if (!system($udev_bin, "check")) { ++if (system($udev_bin, "check")) { + warn "$udev_bin failed to set up the environment, skipping the test"; + exit($EXIT_TEST_SKIP); + } diff --git a/SOURCES/0514-udev-test-fix-missing-directory-test-run.patch b/SOURCES/0514-udev-test-fix-missing-directory-test-run.patch new file mode 100644 index 0000000..5714a16 --- /dev/null +++ b/SOURCES/0514-udev-test-fix-missing-directory-test-run.patch @@ -0,0 +1,35 @@ +From 974431a70775d5127cd973c4b4705d2cf8884011 Mon Sep 17 00:00:00 2001 +From: Alexey Bogdanenko +Date: Sat, 8 Dec 2018 15:35:30 +0300 +Subject: [PATCH] udev-test: fix missing directory test/run + +Fixes the following error: + + Failed to mount test /run: No such file or directory + +By the time command "./test-udev check" calls function "fake_filesystems", +directory "test/run" must be present. + +(cherry picked from commit 1e5548c0e0962424b6ca5fdfd35c866b70760c8f) + +Related: #1642728 +--- + test/udev-test.pl | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 3517feab15..eb76ebd72e 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1558,6 +1558,11 @@ sub udev_setup { + + system("rm", "-rf", "$udev_run"); + ++ if (!mkdir($udev_run)) { ++ warn "unable to create directory $udev_run"; ++ return 0; ++ } ++ + return 1; + } + diff --git a/SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch b/SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch new file mode 100644 index 0000000..67037a2 --- /dev/null +++ b/SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch @@ -0,0 +1,31 @@ +From 57e9ee0f19098d56995955f6692437affdf94041 Mon Sep 17 00:00:00 2001 +From: Alexey Bogdanenko +Date: Tue, 11 Dec 2018 16:55:34 +0300 +Subject: [PATCH] udev-test: check if permitted to create block device nodes + +(cherry picked from commit dbfbc6c4e34366033cb340e8b0c3cbca683ff6f5) + +Related: #1642728 +--- + test/udev-test.pl | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index eb76ebd72e..957cda541c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1554,6 +1554,14 @@ sub udev_setup { + return 0; + } + ++ # check if we are permitted to create block device nodes ++ my $block_device_filename = $udev_dev . "/sda"; ++ if (system("mknod", $block_device_filename, "b", "8", "0")) { ++ warn "unable to create $block_device_filename"; ++ return 0; ++ } ++ unlink $block_device_filename; ++ + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + + system("rm", "-rf", "$udev_run"); diff --git a/SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch b/SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch new file mode 100644 index 0000000..7f712d8 --- /dev/null +++ b/SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch @@ -0,0 +1,45 @@ +From 527d43064a93fae9a4490e5d152b120e91f5eade Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 18 Feb 2019 10:38:29 +0900 +Subject: [PATCH] test-udev: add a testcase of too long line + +(cherry picked from commit 1e797cf596df50a6bdd8cbf8e9b2467a3a934171) + +Related: #1642728 +--- + test/udev-test.pl | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 957cda541c..3a50694fa9 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -39,6 +39,11 @@ for (my $i = 1; $i <= 10000; ++$i) { + $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n"; + } + ++my $rules_10k_tags_continuation = ""; ++for (my $i = 1; $i <= 10000; ++$i) { ++ $rules_10k_tags_continuation .= 'KERNEL=="sda", TAG+="test' . $i . "\"\\\n"; ++} ++ + my @tests = ( + { + desc => "no rules", +@@ -1444,6 +1449,16 @@ EOF + exp_name => "found", + rules => $rules_10k_tags . < "don't crash with lots of tags with continuation", ++ 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 . < +Date: Tue, 19 Feb 2019 09:21:42 +0900 +Subject: [PATCH] test-udev: use proper semantics for too long line with + continuation + +Follow-up for 1e797cf596df50a6bdd8cbf8e9b2467a3a934171. + +(cherry picked from commit e37a5d90b0c624b95f8d0c3400288fec60417ec4) + +Related: #1642728 +--- + test/udev-test.pl | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 3a50694fa9..58b5dc85e1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -39,10 +39,11 @@ for (my $i = 1; $i <= 10000; ++$i) { + $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n"; + } + +-my $rules_10k_tags_continuation = ""; +-for (my $i = 1; $i <= 10000; ++$i) { +- $rules_10k_tags_continuation .= '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 = ( + { diff --git a/SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch b/SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch new file mode 100644 index 0000000..9689d81 --- /dev/null +++ b/SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch @@ -0,0 +1,40 @@ +From 66c41fbbeb472563993724352b1984aa3e7e47db Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 19 Feb 2019 09:22:45 +0900 +Subject: [PATCH] test-udev: add more tests for line continuations and comments + +(cherry picked from commit d35976c670b0e5c2d4081b781e5af88c0689ff00) + +Related: #1642728 +--- + test/udev-test.pl | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 58b5dc85e1..a5e1f8cda3 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1453,13 +1453,21 @@ TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test1 + EOF + }, + { +- desc => "don't crash with lots of tags with continuation", ++ desc => "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 . < +Date: Thu, 21 Feb 2019 18:03:32 +0900 +Subject: [PATCH] test-udev: add more tests for line continuation + +(cherry picked from commit 84a0819c9d89a2ddb195a5d975ae1fd5c62fde3c) + +Related: #1642728 +--- + test/udev-test.pl | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a5e1f8cda3..002fabd9fd 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1466,8 +1466,42 @@ TAG+="hoge1",\\ + TAG+="hoge2",\\ + # spaces before and after token are dropped + TAG+="hoge3", \\ ++\\ ++ \\ + TAG+="hoge4" + TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found" ++EOF ++ }, ++ { ++ desc => "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 => < +Date: Thu, 21 Feb 2019 18:04:12 +0900 +Subject: [PATCH] test-udev: fix alignment and drop unnecessary white spaces + +(cherry picked from commit 3dd2d524141d09d57443ae339e1a77d7ce40f847) + +Related: #1642728 +--- + test/udev-test.pl | 114 +++++++++++++++++++++++----------------------- + 1 file changed, 57 insertions(+), 57 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 002fabd9fd..122359e377 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -49,7 +49,7 @@ 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 => < "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" , ++ not_exp_name => "bad", + rules => $rules_10k_tags_continuation . < +Date: Fri, 5 Jul 2019 11:24:55 -0400 +Subject: [PATCH] test/udev-test.pl: cleanup if skipping test + +In Ubuntu CI, udev-test.pl is run from the debian/test/udev script, +in a test dir created for it; but udev-test.pl setup mounts a +dir, so if it doesn't cleanup/unmount before exiting, the test dir +autopkgtest created for it can't be removed, and autopkgtest +aborts the entire test suite, for example this output (from a +test run inside an armhf container): + +autopkgtest [12:45:36]: test udev: [----------------------- +umount: test/tmpfs: no mount point specified. +mknod: test/tmpfs/dev/null: Operation not permitted +unable to create test/tmpfs/dev/null at ./udev-test.pl line 1611. +Failed to set up the environment, skipping the test at ./udev-test.pl line 1731. +autopkgtest [12:45:41]: test udev: -----------------------] +autopkgtest [12:45:44]: test udev: - - - - - - - - - - results - - - - - - - - - - +udev FAIL non-zero exit status 77 +rm: cannot remove '/tmp/autopkgtest.ocPFA6/autopkgtest_tmp/test/tmpfs': Device or resource busy +autopkgtest [12:46:22]: ERROR: "rm -rf /tmp/autopkgtest.ocPFA6/udev-artifacts /tmp/autopkgtest.ocPFA6/autopkgtest_tmp" failed with stderr "rm: + +(cherry picked from commit abb9cc50afb3949c442849f43301fb33578f3888) + +Related: #1642728 +--- + test/udev-test.pl | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 122359e377..2fea72875b 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1713,6 +1713,12 @@ sub run_test { + + } + ++sub cleanup { ++ system("rm", "-rf", "$udev_run"); ++ system("umount", "$udev_tmpfs"); ++ rmdir($udev_tmpfs); ++} ++ + # only run if we have root permissions + # due to mknod restrictions + if (!($<==0)) { +@@ -1729,11 +1735,13 @@ if ($? >> 8 == 0) { + + if (!udev_setup()) { + warn "Failed to set up the environment, skipping the test"; ++ cleanup(); + exit($EXIT_TEST_SKIP); + } + + if (system($udev_bin, "check")) { + warn "$udev_bin failed to set up the environment, skipping the test"; ++ cleanup(); + exit($EXIT_TEST_SKIP); + } + +@@ -1776,10 +1784,7 @@ if ($list[0]) { + + print "$error errors occurred\n\n"; + +-# cleanup +-system("rm", "-rf", "$udev_run"); +-system("umount", "$udev_tmpfs"); +-rmdir($udev_tmpfs); ++cleanup(); + + if ($error > 0) { + exit(1); diff --git a/SOURCES/0522-test-add-test-cases-for-empty-string-match.patch b/SOURCES/0522-test-add-test-cases-for-empty-string-match.patch new file mode 100644 index 0000000..acf7479 --- /dev/null +++ b/SOURCES/0522-test-add-test-cases-for-empty-string-match.patch @@ -0,0 +1,89 @@ +From 03bc565e6e3249385c4e1ca0ae27670ca2ad9a41 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 11 Sep 2019 09:06:15 +0900 +Subject: [PATCH] test: add test cases for empty string match + +(cherry picked from commit 48d26c90852c22ec94be961f5fbdcf462bb9a6e8) + +Related: #1642728 +--- + test/udev-test.pl | 66 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 66 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 2fea72875b..50d978391b 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1256,6 +1256,72 @@ KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1" + KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2" + KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right" + KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3" ++EOF ++ }, ++ { ++ desc => "test multi matches 5", ++ 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 => < "test multi matches 6", ++ 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 => < "test multi matches 7", ++ 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 => < "test multi matches 8", ++ 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 => < "test multi matches 9", ++ 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 => < "test multi matches 10", ++ 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 => < +Date: Sun, 12 Jul 2020 03:27:45 -0400 +Subject: [PATCH] test: add test case for multi matches when use "||" + +Signed-off-by: gaoyi +(cherry picked from commit 0d3a8bc7ebd76591e14f7098b4266fd2065ac4db) + +Related: #1642728 +--- + test/udev-test.pl | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 50d978391b..4bf97d82bb 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1322,6 +1322,17 @@ EOF + KERNEL=="sda", TAG="" + TAGS=="foo|", SYMLINK+="found" + TAGS=="aaa|bbb", SYMLINK+="bad" ++EOF ++ }, ++ { ++ desc => "test multi matches 11", ++ 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 => < +Date: Fri, 4 Sep 2020 18:09:20 +0200 +Subject: [PATCH] udev-test: do not rely on "mail" group being defined + +"audio" should be there, at least we declare it. "mail" nowadays is less +likely to exist than in the past. + +Fixes one of the items in #16942. + +(cherry picked from commit a9030b81c154c3ec92227d04cad6b13cc1125608) + +Related: #1642728 +--- + test/udev-test.pl | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 4bf97d82bb..a4deffacb9 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -629,9 +629,9 @@ EOF + desc => "textual user/group id", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", +- exp_perms => "root:mail:0660", ++ exp_perms => "root:audio:0660", + rules => < +Date: Fri, 20 Apr 2018 22:38:30 +0200 +Subject: [PATCH] test/udev-test.pl: allow multiple devices per test + +Allow testing cases where multiple devices are added and removed. +This implies a change of the data structure: every test allows +for multiple devices to be added, and "exp_name" etc. are now properties +of the device, not of the test. + +(cherry picked from commit 255c05b72455dcad1b5552d12a813b31f68201a7) + +Related: #1642728 +--- + test/udev-test.pl | 1352 +++++++++++++++++++++++++++++++-------------- + 1 file changed, 929 insertions(+), 423 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a4deffacb9..bd5401da75 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -48,17 +48,28 @@ $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_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda" , ++ exp_rem_error => "yes", ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "sda1" , ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "preserve backslashes, if they are not for a newline", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "aaa", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "aaa", ++ }], + 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ 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" , ++ }], + rules => < "import of shell-value returned from program", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node12345678", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node12345678", ++ }], + 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", ++ desc => "substitution of sysfs value (%s{file})", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/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", ++ devices => [ ++ { ++ 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" , ++ }], + 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ 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" , ++ }], + 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "my-foo8" , ++ }], + rules => < "test substitution by variable name", +- 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", ++ devices => [ ++ { ++ 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", ++ }], + rules => < "test substitution by variable name 2", +- 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", ++ devices => [ ++ { ++ 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", ++ }], + 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", ++ devices => [ ++ { ++ 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" , ++ }], + 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ 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", ++ devices => [ ++ { ++ 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" , ++ }], + rules => < "non matching SUBSYSTEMS for device with no parent", +- devpath => "/devices/virtual/tty/console", +- exp_name => "TTY", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/console", ++ exp_name => "TTY", ++ }], + rules => < "non matching SUBSYSTEMS", +- devpath => "/devices/virtual/tty/console", +- exp_name => "TTY", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/console", ++ exp_name => "TTY" , ++ }], + rules => < "ATTRS match", +- devpath => "/devices/virtual/tty/console", +- exp_name => "foo", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/console", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ 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", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ 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", ++ devices => [ ++ { ++ 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 => < "sysfs parent hierarchy", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "name test with ! in the name", +- devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "is/a/fake/blockdev0", ++ devices => [ ++ { ++ devpath => "/devices/virtual/block/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_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/virtual/block/fake!blockdev0", ++ exp_name => "fake/blockdev0" , ++ exp_rem_error => "yes", ++ }], + rules => < "KERNELS rule", +- 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", ++ devices => [ ++ { ++ 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 => < "KERNELS wildcard all", +- 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", ++ devices => [ ++ { ++ 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 => < "KERNELS wildcard partial", +- 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", ++ devices => [ ++ { ++ 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 => < "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 => < [ ++ { ++ 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 => < "substitute attr with link target value (first match)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-sd", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "driver-is-sd", ++ }], + rules => < "substitute attr with link target value (currently selected device)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-ahci", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "driver-is-ahci", ++ }], + rules => < "ignore ATTRS attribute whitespace", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ignored", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ignored", ++ }], + rules => < "do not ignore ATTRS attribute whitespace", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "matched-with-space", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "matched-with-space", ++ }], + rules => < "permissions USER=bad GROUP=name", +- devpath => "/devices/virtual/tty/tty33", +- exp_name => "tty33", +- exp_perms => "0:0:0600", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/tty33", ++ exp_name => "tty33", ++ exp_perms => "0:0:0600", ++ }], + rules => < "permissions OWNER=1", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "1::0600", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "1::0600", ++ }], + rules => < "permissions GROUP=1", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => ":1:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => ":1:0660", ++ }], + rules => < "textual user id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "daemon::0600", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "daemon::0600", ++ }], + rules => < "textual group id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => ":daemon:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => ":daemon:0660", ++ }], + rules => < "textual user/group id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "root:audio:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "root:audio:0660", ++ }], + rules => < "permissions MODE=0777", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "::0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "::0777", ++ }], + rules => < "permissions OWNER=1 GROUP=1 MODE=0777", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "1:1:0777", ++ }], + rules => < "permissions OWNER to 1", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1::", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1::", ++ }], + rules => < "permissions GROUP to 1", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => ":1:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => ":1:0660", ++ }], + rules => < "permissions MODE to 0060", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "::0060", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "::0060", ++ }], + rules => < "permissions OWNER, GROUP, MODE", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:1:0777", ++ }], + rules => < "permissions only rule", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:1:0777", ++ }], + rules => < "multiple permissions only rule", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:1:0777", ++ }], + rules => < "permissions only rule with override at SYMLINK+ rule", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:2:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:2:0777", ++ }], + rules => < "major/minor number test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_majorminor => "8:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_majorminor => "8:0", ++ }], + rules => < "big major number test", +- devpath => "/devices/virtual/misc/misc-fake1", +- exp_name => "node", +- exp_majorminor => "4095:1", +- rules => < [ ++ { ++ devpath => "/devices/virtual/misc/misc-fake1", ++ exp_name => "node", ++ exp_majorminor => "4095:1", ++ }], ++ rules => < "big major and big minor number test", +- devpath => "/devices/virtual/misc/misc-fake89999", +- exp_name => "node", +- exp_majorminor => "4095:89999", ++ devices => [ ++ { ++ devpath => "/devices/virtual/misc/misc-fake89999", ++ exp_name => "node", ++ exp_majorminor => "4095:89999", ++ }], + rules => < "multiple symlinks with format char", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink2-ttyACM0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "symlink2-ttyACM0", ++ }], + 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 => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "one", ++ not_exp_name => " ", ++ }], + rules => < "symlink with spaces in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with leading space in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with trailing space in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with lots of space in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with multiple spaces in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with space and var with space, part 1", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "first", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "first", ++ not_exp_name => " ", ++ }], + rules => < "symlink with space and var with space, part 2", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with space and var with space, part 3", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "another_symlink", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "another_symlink", ++ not_exp_name => " ", ++ }], + rules => < "symlink creation (same directory)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem0", ++ }], + rules => < "multiple symlinks", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "second-0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ 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", ++ devices => [ ++ { ++ 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", ++ }], + rules => < "symlink node to itself", +- devpath => "/devices/virtual/tty/tty0", +- exp_name => "link", +- exp_add_error => "yes", +- exp_rem_error => "yes", +- option => "clean", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/tty0", ++ exp_name => "link", ++ exp_add_error => "yes", ++ exp_rem_error => "yes", ++ }], ++ option => "clean", + rules => < "symlink %n substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "symlink0", ++ }], + rules => < "symlink %k substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink-ttyACM0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "symlink-ttyACM0", ++ }], + rules => < "symlink %M:%m substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "major-166:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "major-166:0", ++ }], + rules => < "symlink %b substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "symlink-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "symlink-0:0:0:0", ++ }], + rules => < "symlink %c substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "test", ++ }], + rules => < "symlink %c{N} substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "test", ++ }], + rules => < "symlink %c{N+} substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "this", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "this", ++ }], + rules => < "symlink only rule with %c{N+}", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "test", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "test", ++ }], + rules => < "symlink %s{filename} substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "166:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "166:0", ++ }], + rules => < "program result substitution (numbered part of)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "link1", ++ }], + rules => < "program result substitution (numbered part of+)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link4", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "link4", ++ }], + rules => < "SUBSYSTEM match test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ }], + rules => < "DRIVERS match test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ }], + rules => < "devnode substitution test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ }], + rules => < "parent node name substitution test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sda-part-1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "sda-part-1", ++ }], + rules => < "udev_root substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "start-/dev-end", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "start-/dev-end", ++ }], + rules => < "last_rule option", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "last", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "last", ++ }], + rules => < "negation KERNEL!=", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "match", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "match", ++ }], + rules => < "negation SUBSYSTEM!=", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "not-anything", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "not-anything", ++ }], + rules => < "negation PROGRAM!= exit code", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "nonzero-program", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "nonzero-program", ++ }], + rules => < "ENV{} test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test (assign)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test (assign 2 times)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test (assign2)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "part", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "part", ++ }], + rules => < "untrusted string sanitize", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sane", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "sane", ++ }], + rules => < "untrusted string sanitize (don't replace utf8)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "uber", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "uber", ++ }], + rules => < "untrusted string sanitize (replace invalid utf8)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "replaced", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "replaced", ++ }], + rules => < "read sysfs value from parent device", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "serial-354172020305000", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "serial-354172020305000", ++ }], + rules => < "match against empty key string", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ }], + rules => < "check ACTION value", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ }], + rules => < "final assignment", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", +- exp_perms => "root:tty:0640", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ exp_perms => "root:tty:0640", ++ }], + rules => < "final assignment 2", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", +- exp_perms => "root:tty:0640", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ exp_perms => "root:tty:0640", ++ }], + rules => < "env substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node-add-me", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node-add-me", ++ }], + rules => < "reset list to current value", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "three", +- not_exp_name => "two", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "three", ++ not_exp_name => "two", ++ }], + rules => < "test empty SYMLINK+ (empty override)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", +- not_exp_name => "wrong", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ not_exp_name => "wrong", ++ }], + rules => < "test multi matches", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 2", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 3", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 4", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 5", +- 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", ++ desc => "test multi matches 5", ++ devices => [ ++ { ++ 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 => < "test multi matches 6", +- 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", ++ devices => [ ++ { ++ 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 => < "test multi matches 7", +- 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", ++ devices => [ ++ { ++ 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 => < "test multi matches 8", +- 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", ++ devices => [ ++ { ++ 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 => < "test multi matches 9", +- 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", ++ devices => [ ++ { ++ 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 => < "test multi matches 10", +- 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", ++ devices => [ ++ { ++ 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 => < "test multi matches 11", +- 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", ++ devices => [ ++ { ++ 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 => < "IMPORT parent test sequence 1/2 (keep)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "parent", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "parent", ++ }], + option => "keep", + rules => < "IMPORT parent test sequence 2/2 (keep)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "parentenv-parent_right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "parentenv-parent_right", ++ }], + option => "clean", + rules => < "GOTO test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "right", ++ }], + rules => < "GOTO label does not exist", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "right", ++ }], + rules => < "SYMLINK+ compare test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", +- not_exp_name => "wrong", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "right", ++ not_exp_name => "wrong", ++ }], + rules => < "invalid key operation", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "yes", ++ }], + rules => < "operator chars in attribute", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "yes", ++ }], + rules => < "overlong comment line", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "yes", ++ }], + rules => < "magic subsys/kernel lookup", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "00:16:41:e2:8d:ff", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "00:16:41:e2:8d:ff", ++ }], + rules => < "TEST absolute path", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "there", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "there", ++ }], + rules => < "TEST subsys/kernel lookup", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "yes", ++ }], + rules => < "TEST relative path", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "relative", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "relative", ++ }], + rules => < "TEST wildcard substitution (find queue/nr_requests)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found-subdir", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found-subdir", ++ }], + rules => < "TEST MODE=0000", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_perms => "0:0:0000", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda", ++ exp_perms => "0:0:0000", ++ exp_rem_error => "yes", ++ }], + rules => < "TEST PROGRAM feeds OWNER, GROUP, MODE", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_perms => "1:1:0400", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda", ++ exp_perms => "1:1:0400", ++ exp_rem_error => "yes", ++ }], + rules => < "TEST PROGRAM feeds MODE with overflow", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_perms => "0:0:0440", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda", ++ exp_perms => "0:0:0440", ++ exp_rem_error => "yes", ++ }], + rules => < "magic [subsys/sysname] attribute substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda-8741C4G-end", +- exp_perms => "0:0:0600", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda-8741C4G-end", ++ exp_perms => "0:0:0600", ++ }], + rules => < "builtin path_id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", ++ }], + 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", ++ devices => [ ++ { ++ 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 => < "don't crash with lots of tags", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "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", ++ devices => [ ++ { ++ 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", ++ devices => [ ++ { ++ 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", ++ devices => [ ++ { ++ 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 => <{desc}\n"; +- print "device \'$rules->{devpath}\' expecting node/link \'$rules->{exp_name}\'\n"; ++sub check_add { ++ my ($device) = @_; + +- $rc = udev("add", $rules->{devpath}, \$rules->{rules}); +- if ($rc != 0) { +- print "$udev_bin add failed with code $rc\n"; +- $error++; +- } +- if (defined($rules->{not_exp_name})) { +- if ((-e "$udev_dev/$rules->{not_exp_name}") || +- (-l "$udev_dev/$rules->{not_exp_name}")) { +- print "nonexistent: error \'$rules->{not_exp_name}\' not expected to be there\n"; ++ if (defined($device->{not_exp_name})) { ++ if ((-e "$udev_dev/$device->{not_exp_name}") || ++ (-l "$udev_dev/$device->{not_exp_name}")) { ++ print "nonexistent: error \'$device->{not_exp_name}\' not expected to be there\n"; + $error++; + sleep(1); + } + } +- +- if ((-e "$udev_dev/$rules->{exp_name}") || +- (-l "$udev_dev/$rules->{exp_name}")) { ++ if ((-e "$udev_dev/$device->{exp_name}") || ++ (-l "$udev_dev/$device->{exp_name}")) { + + my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, +- $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$rules->{exp_name}"); ++ $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}"); + +- if (defined($rules->{exp_perms})) { +- permissions_test($rules, $uid, $gid, $mode); ++ if (defined($device->{exp_perms})) { ++ permissions_test($device, $uid, $gid, $mode); + } +- if (defined($rules->{exp_majorminor})) { +- major_minor_test($rules, $rdev); ++ if (defined($device->{exp_majorminor})) { ++ major_minor_test($device, $rdev); + } +- print "add: ok\n"; ++ print "add $device->{devpath}: ok\n"; + } else { +- print "add: error"; +- if ($rules->{exp_add_error}) { ++ print "add $device->{devpath}: error"; ++ if ($device->{exp_add_error}) { + print " as expected\n"; + } else { + print "\n"; +@@ -1755,21 +2231,15 @@ sub run_test { + sleep(1); + } + } ++} + +- if (defined($rules->{option}) && $rules->{option} eq "keep") { +- print "\n\n"; +- return; +- } ++sub check_remove { ++ my ($device) = @_; + +- $rc = udev("remove", $rules->{devpath}, \$rules->{rules}); +- if ($rc != 0) { +- print "$udev_bin remove failed with code $rc\n"; +- $error++; +- } +- if ((-e "$udev_dev/$rules->{exp_name}") || +- (-l "$udev_dev/$rules->{exp_name}")) { +- print "remove: error"; +- if ($rules->{exp_rem_error}) { ++ if ((-e "$udev_dev/$device->{exp_name}") || ++ (-l "$udev_dev/$device->{exp_name}")) { ++ print "remove $device->{devpath}: error"; ++ if ($device->{exp_rem_error}) { + print " as expected\n"; + } else { + print "\n"; +@@ -1779,7 +2249,43 @@ sub run_test { + sleep(1); + } + } else { +- print "remove: ok\n"; ++ print "remove $device->{devpath}: ok\n"; ++ } ++} ++ ++sub run_test { ++ my ($rules, $number) = @_; ++ my $rc; ++ my @devices = @{$rules->{devices}}; ++ ++ print "TEST $number: $rules->{desc}\n"; ++ foreach my $dev (@devices) { ++ print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; ++ $rc = udev("add", $dev->{devpath}, \$rules->{rules}); ++ if ($rc != 0) { ++ print "$udev_bin add failed with code $rc\n"; ++ $error++; ++ } ++ } ++ ++ foreach my $dev (@devices) { ++ check_add($dev); ++ } ++ ++ if (defined($rules->{option}) && $rules->{option} eq "keep") { ++ print "\n\n"; ++ return; ++ } ++ ++ foreach my $dev (@devices) { ++ $rc = udev("remove", $dev->{devpath}, \$rules->{rules}); ++ if ($rc != 0) { ++ print "$udev_bin remove failed with code $rc\n"; ++ $error++; ++ } ++ } ++ foreach my $dev (@devices) { ++ check_remove($dev); + } + + print "\n"; diff --git a/SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch b/SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch new file mode 100644 index 0000000..b394455 --- /dev/null +++ b/SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch @@ -0,0 +1,61 @@ +From 9aa12f2f564c208c4c1eaef613d18d1c0b481a16 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Mon, 23 Apr 2018 21:58:12 +0200 +Subject: [PATCH] test/udev-test.pl: create rules only once + +It's not necessary to write the rules for every udev run, as we +now may have many (rather than just 2) per test. + +(cherry picked from commit af7ee3eae689f9c31b49ea13758ad9c901918ce3) + +Related: #1642728 +--- + test/udev-test.pl | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index bd5401da75..8b5a97ad61 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2069,14 +2069,18 @@ EOF + }, + ); + +-sub udev { +- my ($action, $devpath, $rules) = @_; ++sub create_rules { ++ my ($rules) = @_; + + # create temporary rules + system("mkdir", "-p", "$udev_rules_dir"); + open CONF, ">$udev_rules" || die "unable to create rules file: $udev_rules"; + print CONF $$rules; + close CONF; ++} ++ ++sub udev { ++ my ($action, $devpath) = @_; + + if ($valgrind > 0) { + return system("$udev_bin_valgrind $action $devpath"); +@@ -2259,9 +2263,10 @@ sub run_test { + my @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; ++ create_rules(\$rules->{rules}); + foreach my $dev (@devices) { + print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; +- $rc = udev("add", $dev->{devpath}, \$rules->{rules}); ++ $rc = udev("add", $dev->{devpath}); + if ($rc != 0) { + print "$udev_bin add failed with code $rc\n"; + $error++; +@@ -2278,7 +2283,7 @@ sub run_test { + } + + foreach my $dev (@devices) { +- $rc = udev("remove", $dev->{devpath}, \$rules->{rules}); ++ $rc = udev("remove", $dev->{devpath}); + if ($rc != 0) { + print "$udev_bin remove failed with code $rc\n"; + $error++; diff --git a/SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch b/SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch new file mode 100644 index 0000000..f87307f --- /dev/null +++ b/SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch @@ -0,0 +1,169 @@ +From 618d56c7ac8bd8cd701344a0eaca8373a78dea95 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Mon, 23 Apr 2018 21:59:05 +0200 +Subject: [PATCH] test/udev-test.pl: allow concurrent additions and removals + +Allow testing cases where multiple devices are added and removed +simultaneously. Tests are started as synchronously as possible using a +semaphore, in order to test possible race conditions. If this isn't desired, +the test parameter "sleep_us" can be set to the number of microseconds to wait +between udev invocations. + +(cherry picked from commit 09a4062d70b3a10d022e40066e2adf09df05bbbc) + +Related: #1642728 +--- + test/udev-test.pl | 90 +++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 72 insertions(+), 18 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 8b5a97ad61..db25ef13c1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -18,6 +18,10 @@ + + use warnings; + use strict; ++use POSIX qw(WIFEXITED WEXITSTATUS); ++use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); ++use IPC::Semaphore; ++use Time::HiRes qw(usleep); + + my $udev_bin = "./test-udev"; + my $valgrind = 0; +@@ -2210,6 +2214,8 @@ sub check_add { + sleep(1); + } + } ++ ++ print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { + +@@ -2257,21 +2263,72 @@ sub check_remove { + } + } + ++sub run_udev { ++ my ($action, $dev, $sleep_us, $sema) = @_; ++ ++ # Notify main process that this worker has started ++ $sema->op(0, 1, 0); ++ ++ # Wait for start ++ $sema->op(0, 0, 0); ++ usleep($sleep_us) if defined ($sleep_us); ++ my $rc = udev($action, $dev->{devpath}); ++ exit $rc; ++} ++ ++sub fork_and_run_udev { ++ my ($action, $rules, $sema) = @_; ++ my @devices = @{$rules->{devices}}; ++ my $dev; ++ my $k = 0; ++ ++ $sema->setval(0, 1); ++ foreach $dev (@devices) { ++ my $pid = fork(); ++ ++ if (!$pid) { ++ run_udev($action, $dev, ++ defined($rules->{sleep_us}) ? $k * $rules->{sleep_us} : undef, ++ $sema); ++ } else { ++ $dev->{pid} = $pid; ++ } ++ $k++; ++ } ++ ++ # This operation waits for all workers to become ready, and ++ # starts them off when that's the case. ++ $sema->op(0, -($#devices + 2), 0); ++ ++ foreach $dev (@devices) { ++ my $rc; ++ my $pid; ++ ++ $pid = waitpid($dev->{pid}, 0); ++ if ($pid == -1) { ++ print "error waiting for pid dev->{pid}\n"; ++ $error += 1; ++ } ++ if (WIFEXITED($?)) { ++ $rc = WEXITSTATUS($?); ++ ++ if ($rc) { ++ print "$udev_bin $action for $dev->{devpath} failed with code $rc\n"; ++ $error += 1; ++ } ++ } ++ } ++} ++ + sub run_test { +- my ($rules, $number) = @_; ++ my ($rules, $number, $sema) = @_; + my $rc; + my @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); +- foreach my $dev (@devices) { +- print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; +- $rc = udev("add", $dev->{devpath}); +- if ($rc != 0) { +- print "$udev_bin add failed with code $rc\n"; +- $error++; +- } +- } ++ ++ fork_and_run_udev("add", $rules, $sema); + + foreach my $dev (@devices) { + check_add($dev); +@@ -2282,13 +2339,8 @@ sub run_test { + return; + } + +- foreach my $dev (@devices) { +- $rc = udev("remove", $dev->{devpath}); +- if ($rc != 0) { +- print "$udev_bin remove failed with code $rc\n"; +- $error++; +- } +- } ++ fork_and_run_udev("remove", $rules, $sema); ++ + foreach my $dev (@devices) { + check_remove($dev); + } +@@ -2350,12 +2402,13 @@ foreach my $arg (@ARGV) { + push(@list, $arg); + } + } ++my $sema = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT); + + if ($list[0]) { + foreach my $arg (@list) { + if (defined($tests[$arg-1]->{desc})) { + print "udev-test will run test number $arg:\n\n"; +- run_test($tests[$arg-1], $arg); ++ run_test($tests[$arg-1], $arg, $sema); + } else { + print "test does not exist.\n"; + } +@@ -2365,11 +2418,12 @@ if ($list[0]) { + print "\nudev-test will run ".($#tests + 1)." tests:\n\n"; + + foreach my $rules (@tests) { +- run_test($rules, $test_num); ++ run_test($rules, $test_num, $sema); + $test_num++; + } + } + ++$sema->remove; + print "$error errors occurred\n\n"; + + cleanup(); diff --git a/SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch b/SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch new file mode 100644 index 0000000..a20ebbd --- /dev/null +++ b/SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch @@ -0,0 +1,260 @@ +From 5f34ea55a8c6723240eb1641a655db7df3c428a2 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 09:38:26 +0200 +Subject: [PATCH] test/udev-test.pl: use computed devnode name + +More often than not, the created devnode is the basename of the +sysfs entry. The "devnode" device may be used to override the +auto-detected node name. + +Permissions and major/minor number are now verified on the devnode +itself, not on symlinks. + +For those tests where exp_name is set to the computed devnode name, +the explicit "exp_name" can be removed. "exp_name" is only required for +symlinks. + +This allows separate testing for devnodes and symlinks an a follow-up +patch. + +(cherry picked from commit f0dccf01a7b4e72278e14effd74782ea83d0a73b) + +Related: #1642728 +--- + test/udev-test.pl | 92 +++++++++++++++++++++++++++++++++-------------- + 1 file changed, 66 insertions(+), 26 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index db25ef13c1..aa9a8dc2ff 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -55,12 +55,10 @@ my @tests = ( + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda" , + exp_rem_error => "yes", + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sda1" , + exp_rem_error => "yes", + }], + rules => < [ + { + devpath => "/devices/virtual/block/fake!blockdev0", ++ devnode => "fake/blockdev0", + exp_name => "is/a/fake/blockdev0" , + }], + rules => < [ + { + devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "fake/blockdev0" , ++ devnode => "fake/blockdev0", + exp_rem_error => "yes", + }], + rules => < [ + { + devpath => "/devices/virtual/tty/tty33", +- exp_name => "tty33", + exp_perms => "0:0:0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1::", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => ":1:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "::0060", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:2:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", + exp_perms => "0:0:0000", + exp_rem_error => "yes", + }], +@@ -1935,7 +1925,6 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", + exp_perms => "1:1:0400", + exp_rem_error => "yes", + }], +@@ -1949,7 +1938,6 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", + exp_perms => "0:0:0440", + exp_rem_error => "yes", + }], +@@ -2203,6 +2191,44 @@ sub udev_setup { + return 1; + } + ++sub get_devnode { ++ my ($device) = @_; ++ my $devnode; ++ ++ if (defined($device->{devnode})) { ++ $devnode = "$udev_dev/$device->{devnode}"; ++ } else { ++ $devnode = "$device->{devpath}"; ++ $devnode =~ s!.*/!$udev_dev/!; ++ } ++ return $devnode; ++} ++ ++sub check_devnode { ++ my ($device) = @_; ++ my $devnode = get_devnode($device); ++ ++ my @st = lstat("$devnode"); ++ if (! (-b _ || -c _)) { ++ print "add $devnode: error\n"; ++ system("tree", "$udev_dev"); ++ $error++; ++ return undef; ++ } ++ ++ my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, ++ $atime, $mtime, $ctime, $blksize, $blocks) = @st; ++ ++ if (defined($device->{exp_perms})) { ++ permissions_test($device, $uid, $gid, $mode); ++ } ++ if (defined($device->{exp_majorminor})) { ++ major_minor_test($device, $rdev); ++ } ++ print "add $devnode: ok\n"; ++ return $devnode; ++} ++ + sub check_add { + my ($device) = @_; + +@@ -2215,19 +2241,13 @@ sub check_add { + } + } + ++ my $devnode = check_devnode($device); ++ + print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; ++ return if (!defined($device->{exp_name})); ++ + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { +- +- my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, +- $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}"); +- +- if (defined($device->{exp_perms})) { +- permissions_test($device, $uid, $gid, $mode); +- } +- if (defined($device->{exp_majorminor})) { +- major_minor_test($device, $rdev); +- } + print "add $device->{devpath}: ok\n"; + } else { + print "add $device->{devpath}: error"; +@@ -2243,12 +2263,32 @@ sub check_add { + } + } + ++sub check_remove_devnode { ++ my ($device) = @_; ++ my $devnode = get_devnode($device); ++ ++ if (-e "$devnode") { ++ print "remove $devnode: error"; ++ print "\n"; ++ system("tree", "$udev_dev"); ++ print "\n"; ++ $error++; ++ sleep(1); ++ } else { ++ print "remove $devnode: ok\n"; ++ } ++} ++ + sub check_remove { + my ($device) = @_; + ++ check_remove_devnode($device); ++ ++ return if (!defined($device->{exp_name})); ++ + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { +- print "remove $device->{devpath}: error"; ++ print "remove $device->{exp_name}: error"; + if ($device->{exp_rem_error}) { + print " as expected\n"; + } else { +@@ -2259,7 +2299,7 @@ sub check_remove { + sleep(1); + } + } else { +- print "remove $device->{devpath}: ok\n"; ++ print "remove $device->{exp_name}: ok\n"; + } + } + diff --git a/SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch b/SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch new file mode 100644 index 0000000..3c6e624 --- /dev/null +++ b/SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch @@ -0,0 +1,61 @@ +From 8ee1cc626f616a2022d641a464fbde9108dd8ad9 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 10:50:24 +0200 +Subject: [PATCH] test/udev-test.pl: test correctness of symlink targets + +Test if symlinks are created correctly by comparing the symlink +targets to the devnode path. This implies (for the symlink) that +major/minor numbers and permissions are correct, as we have tested +that on the devnode already. + +(cherry picked from commit 997683c8f152e1c139a7ce537de81a0aeae4627f) + +Related: #1642728 +--- + test/udev-test.pl | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index aa9a8dc2ff..2e3089c5e0 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -22,6 +22,7 @@ use POSIX qw(WIFEXITED WEXITSTATUS); + use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); + use IPC::Semaphore; + use Time::HiRes qw(usleep); ++use Cwd qw(getcwd abs_path); + + my $udev_bin = "./test-udev"; + my $valgrind = 0; +@@ -2243,14 +2244,26 @@ sub check_add { + + my $devnode = check_devnode($device); + +- print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; + return if (!defined($device->{exp_name})); + +- if ((-e "$udev_dev/$device->{exp_name}") || +- (-l "$udev_dev/$device->{exp_name}")) { +- print "add $device->{devpath}: ok\n"; ++ my @st = lstat("$udev_dev/$device->{exp_name}"); ++ if (-l _) { ++ my $cwd = getcwd(); ++ my $dir = "$udev_dev/$device->{exp_name}"; ++ $dir =~ s!/[^/]*$!!; ++ my $tgt = readlink("$udev_dev/$device->{exp_name}"); ++ $tgt = abs_path("$dir/$tgt"); ++ $tgt =~ s!^$cwd/!!; ++ ++ if ($tgt ne $devnode) { ++ print "symlink $device->{exp_name}: error, found -> $tgt\n"; ++ $error++; ++ system("tree", "$udev_dev"); ++ } else { ++ print "symlink $device->{exp_name}: ok\n"; ++ } + } else { +- print "add $device->{devpath}: error"; ++ print "symlink $device->{exp_name}: error"; + if ($device->{exp_add_error}) { + print " as expected\n"; + } else { diff --git a/SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch b/SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch new file mode 100644 index 0000000..f004054 --- /dev/null +++ b/SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch @@ -0,0 +1,1607 @@ +From fb8d10456d7d5a085e1adb5bfd45f1cda813ac22 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 17:15:58 +0200 +Subject: [PATCH] test/udev-test.pl: allow checking multiple symlinks + +Instead of testing the existence or non-exisitence of just a single +symlink, allow testing of several links per device. + +Change the test definitions accordingly. + +(cherry picked from commit e62acc3159935781f05fa59c48e5a74e85c61ce2) + +Related: #1642728 +--- + test/udev-test.pl | 495 +++++++++++++++++++++++++++------------------- + 1 file changed, 296 insertions(+), 199 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 2e3089c5e0..f5edecefd0 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -71,7 +71,7 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_links => ["boot_disk"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_links => ["boot_disk"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_links => ["boot_disk"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1", "boot_disk1-4", "boot_disk1-5"], ++ not_exp_links => ["boot_disk1-1", "boot_disk1-2", "boot_disk1-3"] + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1"], ++ not_exp_links => ["boot_diskX1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1", "boot_diskXY1"], ++ not_exp_links => ["boot_diskXX1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0", "catch-all"], + }], + rules => < "catch device by * - take 2", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0"], ++ not_exp_links => ["bad"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0"], ++ not_exp_links => ["modem/0-1", "modem/0-2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0"], ++ not_exp_links => ["modem/0-1", "modem/0-2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "whitespace" , ++ exp_links => ["whitespace"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "aaa", ++ exp_links => ["aaa"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "sub/direct/ory/modem" , ++ exp_links => ["sub/direct/ory/modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "first_disk5" , ++ exp_links => ["first_disk5"], + }], + rules => < [ + { + 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_links => ["Major:8:minor:5:kernelnumber:5:id:0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node12345678", ++ exp_links => ["node12345678"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk-ATA-sda" , ++ exp_links => ["disk-ATA-sda"], ++ not_exp_links => ["modem"], + }], + rules => < [ + { + 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_links => ["special-device-5"], ++ not_exp_links => ["not"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "newline_removed" , ++ exp_links => ["newline_removed"], + }], + rules => < [ + { + 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_links => ["test-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo9" , ++ exp_links => ["foo9"], ++ not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "bar9" , ++ exp_links => ["bar9"], ++ not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo7" , ++ exp_links => ["foo7"], ++ not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo8"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_links => ["foo2"], ++ not_exp_links => ["foo1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_links => ["foo2"], ++ not_exp_links => ["foo1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_links => ["foo2"], ++ not_exp_links => ["foo1", "foo3"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo9" , ++ exp_links => ["my-foo9"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo8" , ++ exp_links => ["my-foo8"], ++ not_exp_links => ["my-foo3", "my-foo4", "my-foo5", "my-foo6", "my-foo7", "my-foo9"], + }], + rules => < [ + { + 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_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], + }], + rules => < [ + { + 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_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], + }], + rules => < [ + { + 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_links => ["850:0:0:05"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "855" , ++ exp_links => ["855"], + }], + rules => < [ + { + 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_links => ["8550:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/virtual/tty/console", +- exp_name => "TTY", ++ exp_links => ["TTY"], ++ not_exp_links => ["foo"], + }], + rules => < [ + { + devpath => "/devices/virtual/tty/console", +- exp_name => "TTY" , ++ exp_links => ["TTY"], ++ not_exp_links => ["foo"], + }], + rules => < [ + { + devpath => "/devices/virtual/tty/console", +- exp_name => "foo" , ++ exp_links => ["foo", "TTY"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "empty" , ++ exp_links => ["empty", "not-something"], ++ not_exp_links => ["something", "not-empty"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "non-existent" , ++ exp_links => ["non-existent", "wrong"], ++ not_exp_links => ["something", "empty", "not-empty", ++ "not-something", "something"], + }], + rules => < [ + { + 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_links => ["scsi-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < "/devices/virtual/block/fake!blockdev0", + devnode => "fake/blockdev0", +- exp_name => "is/a/fake/blockdev0" , ++ exp_links => ["is/a/fake/blockdev0"], ++ not_exp_links => ["is/not/a/fake/blockdev0", "modem"], + }], + rules => < "/devices/virtual/block/fake!blockdev0", + devnode => "fake/blockdev0", +- exp_rem_error => "yes", ++ not_exp_links => ["modem"], + }], + rules => < [ + { + 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_links => ["scsi-0:0:0:0"], ++ not_exp_links => ["no-match", "short-id", "not-scsi"], + }], + rules => < [ + { + 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_links => ["scsi-0:0:0:0"], ++ not_exp_links => ["no-match", "before"], + }], + rules => < [ + { + 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_links => ["scsi-0:0:0:0", "before"], + }], + rules => < [ + { + 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_links => ["scsi-0:0:0:0", "before"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-sd", ++ exp_links => ["driver-is-sd"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-ahci", ++ exp_links => ["driver-is-ahci"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ignored", ++ exp_links => ["ignored"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "matched-with-space", ++ exp_links => ["matched-with-space"], ++ not_exp_links => ["wrong-to-ignore"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "1::0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => ":1:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "daemon::0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => ":daemon:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "root:audio:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "::0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_majorminor => "8:0", + }], + rules => < [ + { + devpath => "/devices/virtual/misc/misc-fake1", +- exp_name => "node", ++ exp_links => ["node"], + exp_majorminor => "4095:1", + }], + rules => < [ + { + devpath => "/devices/virtual/misc/misc-fake89999", +- exp_name => "node", ++ exp_links => ["node"], + exp_majorminor => "4095:89999", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink2-ttyACM0", ++ exp_links => ["symlink1-0", "symlink2-ttyACM0", "symlink3-"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "one", +- not_exp_name => " ", ++ exp_links => ["one", "two"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < "symlink with space and var with space, part 1", ++ desc => "symlink with space and var with space", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "first", +- not_exp_name => " ", ++ exp_links => ["first"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "another_symlink", +- not_exp_name => " ", ++ exp_links => ["another_symlink"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem0", ++ exp_links => ["modem0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "second-0" , ++ exp_links => ["first-0", "second-0", "third-0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => ".", ++ exp_links => ["."], + exp_add_error => "yes", + exp_rem_error => "yes", + }], +@@ -1148,7 +1176,7 @@ EOF + devices => [ + { + devpath => "/devices/virtual/tty/tty0", +- exp_name => "link", ++ exp_links => ["link"], + exp_add_error => "yes", + exp_rem_error => "yes", + }], +@@ -1162,7 +1190,7 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink0", ++ exp_links => ["symlink0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink-ttyACM0", ++ exp_links => ["symlink-ttyACM0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "major-166:0", ++ exp_links => ["major-166:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "symlink-0:0:0:0", ++ exp_links => ["symlink-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ exp_links => ["test"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ exp_links => ["test"], ++ not_exp_links => ["symlink", "this"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "this", ++ exp_links => ["test", "this"], ++ not_exp_links => ["symlink"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "test", ++ exp_links => ["test", "this"], ++ not_exp_links => ["symlink"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "166:0", ++ exp_links => ["166:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link1", ++ exp_links => ["link1", "link2"], ++ not_exp_links => ["node"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link4", ++ exp_links => ["link1", "link2", "link3", "link4"], ++ not_exp_links => ["node"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], ++ not_exp_links => ["should_not_match", "should_not_match2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], ++ not_exp_links => ["should_not_match"] + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sda-part-1", ++ exp_links => ["sda-part-1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "start-/dev-end", ++ exp_links => ["start-/dev-end"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "last", ++ exp_links => ["last"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "match", ++ exp_links => ["match", "before"], ++ not_exp_links => ["matches-but-is-negated"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "not-anything", ++ exp_links => ["before", "not-anything"], ++ not_exp_links => ["matches-but-is-negated"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "nonzero-program", ++ exp_links => ["before", "nonzero-program"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true"], ++ not_exp_links => ["bad", "wrong"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true"], ++ not_exp_links => ["bad", "wrong", "no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true", "before"], ++ not_exp_links => ["no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true", "before"], ++ not_exp_links => ["no", "bad"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "part", +- }], ++ exp_links => ["part"], ++ not_exp_links => ["disk"], ++ }, ++ ], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sane", ++ exp_links => ["sane"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "uber", ++ exp_links => ["uber"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "replaced", ++ exp_links => ["replaced"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "serial-354172020305000", ++ exp_links => ["serial-354172020305000"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], ++ not_exp_links => ["not-1-ok", "not-2-ok", "not-3-ok"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], ++ not_exp_links => ["unknown-not-ok"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], + exp_perms => "root:tty:0640", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], + exp_perms => "root:tty:0640", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node-add-me", ++ exp_links => ["node-add-me"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "three", +- not_exp_name => "two", ++ exp_links => ["three"], ++ not_exp_links => ["two", "one"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", +- not_exp_name => "wrong", ++ exp_links => ["right"], ++ not_exp_links => ["wrong"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right", "before"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right", "before"], ++ not_exp_links => ["nomatch"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right"], ++ not_exp_links => ["nomatch", "wrong1", "wrong2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right"], ++ not_exp_links => ["nomatch", "wrong1", "wrong2", "wrong3"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "parent", ++ exp_links => ["parent"], + }], + option => "keep", + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "parentenv-parent_right", ++ exp_links => ["parentenv-parent_right"], + }], + option => "clean", + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ exp_links => ["right"], ++ not_exp_test => ["wrong", "wrong2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ exp_links => ["right"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", +- not_exp_name => "wrong", ++ exp_links => ["right", "link"], ++ not_exp_links => ["wrong"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ exp_links => ["yes"], ++ not_exp_links => ["no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ exp_links => ["yes"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ exp_links => ["yes"], ++ not_exp_links => ["no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "00:16:41:e2:8d:ff", ++ exp_links => ["00:16:41:e2:8d:ff"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "there", ++ exp_links => ["there"], ++ not_exp_links => ["notthere"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ exp_links => ["yes"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "relative", ++ exp_links => ["relative"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found-subdir", ++ exp_links => ["found-subdir"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda-8741C4G-end", ++ exp_links => ["sda-8741C4G-end"], + exp_perms => "0:0:0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", ++ exp_links => ["disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0"], + }], + rules => < [ + { + 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" , ++ exp_links => ["found"], ++ not_exp_links => ["bad"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + }], + rules => $rules_10k_tags . < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => $rules_10k_tags_continuation . < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + + }], +@@ -2046,7 +2098,7 @@ TAGS=="aaa", SYMLINK+="bad" + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => <{not_exp_name})) { +- if ((-e "$udev_dev/$device->{not_exp_name}") || +- (-l "$udev_dev/$device->{not_exp_name}")) { +- print "nonexistent: error \'$device->{not_exp_name}\' not expected to be there\n"; +- $error++; +- sleep(1); +- } +- } ++sub get_link_target { ++ my ($link) = @_; + +- my $devnode = check_devnode($device); ++ my $cwd = getcwd(); ++ my $dir = "$udev_dev/$link"; ++ my $tgt = readlink("$udev_dev/$link"); ++ $dir =~ s!/[^/]*$!!; ++ $tgt = abs_path("$dir/$tgt"); ++ $tgt =~ s!^$cwd/!!; ++ return $tgt; ++} + +- return if (!defined($device->{exp_name})); ++sub check_link_add { ++ my ($link, $devnode, $err_expected) = @_; + +- my @st = lstat("$udev_dev/$device->{exp_name}"); ++ my @st = lstat("$udev_dev/$link"); + if (-l _) { +- my $cwd = getcwd(); +- my $dir = "$udev_dev/$device->{exp_name}"; +- $dir =~ s!/[^/]*$!!; +- my $tgt = readlink("$udev_dev/$device->{exp_name}"); +- $tgt = abs_path("$dir/$tgt"); +- $tgt =~ s!^$cwd/!!; ++ my $tgt = get_link_target($link); + + if ($tgt ne $devnode) { +- print "symlink $device->{exp_name}: error, found -> $tgt\n"; ++ print "symlink $link: error, found -> $tgt\n"; + $error++; + system("tree", "$udev_dev"); + } else { +- print "symlink $device->{exp_name}: ok\n"; ++ print "symlink $link: ok\n"; + } + } else { +- print "symlink $device->{exp_name}: error"; +- if ($device->{exp_add_error}) { ++ print "symlink $link: error"; ++ if ($err_expected) { + print " as expected\n"; + } else { + print "\n"; +@@ -2276,6 +2322,49 @@ sub check_add { + } + } + ++sub check_link_nonexistent { ++ my ($link, $devnode, $err_expected) = @_; ++ ++ if ((-e "$udev_dev/$link") || (-l "$udev_dev/$link")) { ++ my $tgt = get_link_target($link); ++ ++ if ($tgt ne $devnode) { ++ print "nonexistent: '$link' points to other device (ok)\n"; ++ } else { ++ print "nonexistent: error \'$link\' should not be there"; ++ if ($err_expected) { ++ print " (as expected)\n"; ++ } else { ++ print "\n"; ++ system("tree", "$udev_dev"); ++ print "\n"; ++ $error++; ++ sleep(1); ++ } ++ } ++ } else { ++ print "nonexistent $link: ok\n"; ++ } ++} ++ ++sub check_add { ++ my ($device) = @_; ++ my $devnode = check_devnode($device); ++ ++ if (defined($device->{exp_links})) { ++ foreach my $link (@{$device->{exp_links}}) { ++ check_link_add($link, $devnode, ++ $device->{exp_add_error}); ++ } ++ } ++ if (defined $device->{not_exp_links}) { ++ foreach my $link (@{$device->{not_exp_links}}) { ++ check_link_nonexistent($link, $devnode, ++ $device->{exp_nodev_error}); ++ } ++ } ++} ++ + sub check_remove_devnode { + my ($device) = @_; + my $devnode = get_devnode($device); +@@ -2292,17 +2381,13 @@ sub check_remove_devnode { + } + } + +-sub check_remove { +- my ($device) = @_; ++sub check_link_remove { ++ my ($link, $err_expected) = @_; + +- check_remove_devnode($device); +- +- return if (!defined($device->{exp_name})); +- +- if ((-e "$udev_dev/$device->{exp_name}") || +- (-l "$udev_dev/$device->{exp_name}")) { +- print "remove $device->{exp_name}: error"; +- if ($device->{exp_rem_error}) { ++ if ((-e "$udev_dev/$link") || ++ (-l "$udev_dev/$link")) { ++ print "remove $link: error"; ++ if ($err_expected) { + print " as expected\n"; + } else { + print "\n"; +@@ -2312,7 +2397,19 @@ sub check_remove { + sleep(1); + } + } else { +- print "remove $device->{exp_name}: ok\n"; ++ print "remove $link: ok\n"; ++ } ++} ++ ++sub check_remove { ++ my ($device) = @_; ++ ++ check_remove_devnode($device); ++ ++ return if (!defined($device->{exp_links})); ++ ++ foreach my $link (@{$device->{exp_links}}) { ++ check_link_remove($link, $device->{exp_rem_error}); + } + } + diff --git a/SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch b/SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch new file mode 100644 index 0000000..9c2ee94 --- /dev/null +++ b/SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch @@ -0,0 +1,83 @@ +From 0e0b90ffcf0731865846bfa2754a809cc2b8c53e Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 17:57:47 +0200 +Subject: [PATCH] test/udev-test.pl: fix wrong test descriptions + +udev hasn't supported renaming device nodes for some time. + +(cherry picked from commit 46bc71b2b73f8a1e27dc5e142730e9877dd05e3e) + +Related: #1642728 +--- + test/udev-test.pl | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index f5edecefd0..d5d0e130e3 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -212,7 +212,7 @@ KERNEL=="ttyACM[0-9]*", SYMLINK+="modem/%n" + EOF + }, + { +- desc => "replace kernel name", ++ desc => "don't replace kernel name", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -223,7 +223,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle comment lines in config file (and replace kernel name)", ++ desc => "Handle comment lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -236,7 +236,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle comment lines in config file with whitespace (and replace kernel name)", ++ desc => "Handle comment lines in config file with whitespace (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -249,7 +249,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle whitespace only lines (and replace kernel name)", ++ desc => "Handle whitespace only lines (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -267,7 +267,7 @@ KERNEL=="ttyACM0", SYMLINK+="whitespace" + EOF + }, + { +- desc => "Handle empty lines in config file (and replace kernel name)", ++ desc => "Handle empty lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -280,7 +280,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle backslashed multi lines in config file (and replace kernel name)", ++ desc => "Handle backslashed multi lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -303,8 +303,9 @@ EOF + KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \\101", RESULT=="A", SYMLINK+="aaa" + EOF + }, ++ # 20 + { +- desc => "Handle stupid backslashed multi lines in config file (and replace kernel name)", ++ desc => "Handle stupid backslashed multi lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", diff --git a/SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch b/SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch new file mode 100644 index 0000000..72021fb --- /dev/null +++ b/SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch @@ -0,0 +1,35 @@ +From 2d0b828715e67f7accda6f73481deb74febebcb6 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 18:08:18 +0200 +Subject: [PATCH] test/udev-test.pl: last_rule is unsupported + +the "last_rule" option hasn't been supported for some time. +Therefore this test fails if a "not_exp_links" attribute is added, +as it should be. Mark it appropriately. + +(cherry picked from commit 17cce031531a5d3f38a27374c99d1bdba5959dbd) + +Related: #1642728 +--- + test/udev-test.pl | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index d5d0e130e3..a9c2dd95f1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1373,11 +1373,14 @@ SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="start-%r-end" + EOF + }, + { ++ # This is not supported any more + desc => "last_rule option", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["last"], ++ not_exp_links => ["very-last"], ++ exp_nodev_error => "yes", + }], + rules => < +Date: Tue, 24 Apr 2018 18:09:50 +0200 +Subject: [PATCH] test/udev-test.pl: Make some tests a little harder + +Add some rules that make it a bit harder to pass, mainly the +non-existence checks. + +(cherry picked from commit 06d4d4e24e7d0b51120b165e540d278842e8b1a3) + +Related: #1642728 +--- + test/udev-test.pl | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a9c2dd95f1..7465b5859e 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1358,7 +1358,7 @@ EOF + exp_links => ["sda-part-1"], + }], + rules => < ["part"], + not_exp_links => ["disk"], + }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_links => ["disk"], ++ not_exp_links => ["part"], ++ }, + ], + rules => < < < "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_perms => "1:1:0400", +- exp_rem_error => "yes", + }], + rules => < +Date: Tue, 24 Apr 2018 18:16:59 +0200 +Subject: [PATCH] test/udev-test.pl: remove bogus rules from magic subsys test + +These rules have survived from an ancient version of the code +and save no purpose any more. + +(cherry picked from commit 86634df43b715f3f77c7de73a3ef6566e5cdf571) + +Related: #1642728 +--- + test/udev-test.pl | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 7465b5859e..6928439d14 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2017,8 +2017,6 @@ EOF + exp_perms => "0:0:0600", + }], + rules => < +Date: Tue, 24 Apr 2018 18:27:25 +0200 +Subject: [PATCH] test/udev-test.pl: merge "space and var with space" tests + +As we can check multiple links in a single test now, these 3 +tests can be merged into one. + +(cherry picked from commit 2084fe0d3290c525ecb9faa07d07c3abc2488e59) + +Related: #1642728 +--- + test/udev-test.pl | 31 +++---------------------------- + 1 file changed, 3 insertions(+), 28 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 6928439d14..880a73b10b 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1103,34 +1103,9 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_links => ["first"], +- not_exp_links => [" "], +- }], +- rules => < "symlink with space and var with space, part 2", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_links => ["name-one_two_three-end"], +- not_exp_links => [" "], +- }], +- rules => < "symlink with space and var with space, part 3", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_links => ["another_symlink"], +- not_exp_links => [" "], ++ exp_links => ["first", "name-one_two_three-end", ++ "another_symlink", "a", "b", "c"], ++ not_exp_links => [" "], + }], + rules => < +Date: Tue, 24 Apr 2018 18:30:09 +0200 +Subject: [PATCH] test/udev-test.pl: merge import parent tests into one + +As we can test multiple devices and multiple links per device +in one test now, these two tests can be merged into one. + +(cherry picked from commit a96cd21d31cb7af211862768e133b50b085634e7) + +Related: #1642728 +--- + test/udev-test.pl | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 880a73b10b..0344d2e89c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1781,28 +1781,21 @@ TAGS=="aaa||bbb||ccc", SYMLINK+="bad" + EOF + }, + { +- desc => "IMPORT parent test sequence 1/2 (keep)", ++ desc => "IMPORT parent test", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_links => ["parent"], +- }], +- option => "keep", +- rules => < "IMPORT parent test sequence 2/2 (keep)", +- devices => [ ++ }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["parentenv-parent_right"], + }], +- option => "clean", ++ sleep_us => 500000, # Serialized! We need to sleep here after adding sda + rules => < +Date: Tue, 24 Apr 2018 20:55:01 +0200 +Subject: [PATCH] test/udev-test.pl: count "good" results + +This is helpful to catch possible regressions in the test. +Also, don't count wait() errors, they are likely not udev errors. + +(cherry picked from commit b95c43982ab7d0253b552ad56cffb3d68fcbb4f6) + +Related: #1642728 +--- + test/udev-test.pl | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 0344d2e89c..813be70739 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2114,6 +2114,7 @@ sub udev { + } + + my $error = 0; ++my $good = 0; + + sub permissions_test { + my($rules, $uid, $gid, $mode) = @_; +@@ -2144,6 +2145,7 @@ sub permissions_test { + } + if ($wrong == 0) { + print "permissions: ok\n"; ++ $good++; + } else { + printf " expected permissions are: %s:%s:%#o\n", $1, $2, oct($3); + printf " created permissions are : %i:%i:%#o\n", $uid, $gid, $mode & 07777; +@@ -2169,6 +2171,7 @@ sub major_minor_test { + } + if ($wrong == 0) { + print "major:minor: ok\n"; ++ $good++; + } else { + printf " expected major:minor is: %i:%i\n", $1, $2; + printf " created major:minor is : %i:%i\n", $major, $minor; +@@ -2254,6 +2257,7 @@ sub check_devnode { + major_minor_test($device, $rdev); + } + print "add $devnode: ok\n"; ++ $good++; + return $devnode; + } + +@@ -2282,11 +2286,13 @@ sub check_link_add { + system("tree", "$udev_dev"); + } else { + print "symlink $link: ok\n"; ++ $good++; + } + } else { + print "symlink $link: error"; + if ($err_expected) { + print " as expected\n"; ++ $good++; + } else { + print "\n"; + system("tree", "$udev_dev"); +@@ -2305,10 +2311,12 @@ sub check_link_nonexistent { + + if ($tgt ne $devnode) { + print "nonexistent: '$link' points to other device (ok)\n"; ++ $good++; + } else { + print "nonexistent: error \'$link\' should not be there"; + if ($err_expected) { + print " (as expected)\n"; ++ $good++; + } else { + print "\n"; + system("tree", "$udev_dev"); +@@ -2319,6 +2327,7 @@ sub check_link_nonexistent { + } + } else { + print "nonexistent $link: ok\n"; ++ $good++; + } + } + +@@ -2353,6 +2362,7 @@ sub check_remove_devnode { + sleep(1); + } else { + print "remove $devnode: ok\n"; ++ $good++; + } + } + +@@ -2364,6 +2374,7 @@ sub check_link_remove { + print "remove $link: error"; + if ($err_expected) { + print " as expected\n"; ++ $good++; + } else { + print "\n"; + system("tree", "$udev_dev"); +@@ -2373,6 +2384,7 @@ sub check_link_remove { + } + } else { + print "remove $link: ok\n"; ++ $good++; + } + } + +@@ -2432,7 +2444,6 @@ sub fork_and_run_udev { + $pid = waitpid($dev->{pid}, 0); + if ($pid == -1) { + print "error waiting for pid dev->{pid}\n"; +- $error += 1; + } + if (WIFEXITED($?)) { + $rc = WEXITSTATUS($?); +@@ -2440,6 +2451,8 @@ sub fork_and_run_udev { + if ($rc) { + print "$udev_bin $action for $dev->{devpath} failed with code $rc\n"; + $error += 1; ++ } else { ++ $good++; + } + } + } +@@ -2549,7 +2562,7 @@ if ($list[0]) { + } + + $sema->remove; +-print "$error errors occurred\n\n"; ++print "$error errors occurred. $good good results.\n\n"; + + cleanup(); + diff --git a/SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch b/SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch new file mode 100644 index 0000000..e01add4 --- /dev/null +++ b/SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch @@ -0,0 +1,199 @@ +From 8ab9d11b925e7f39b350ce69a1e28752de411b35 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 22:04:55 +0200 +Subject: [PATCH] tests/udev-test.pl: add multiple device test + +Add 4 new tests using multiple devices. Number 2-4 use many +devices claiming the same symlink, where only one device has +a higher priority thatn the others. They fail sporadically with +the current code, if a race condition causes the symlink to point +to the wrong device. Test 4 is like test 2 with sleeps in between, +it's much less likely to fail. + +(cherry picked from commit 4a0ec82daf32446519e1d86329bb802325b82104) + +Related: #1642728 +--- + test/udev-test.pl | 169 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 169 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 813be70739..d964c664b6 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2085,6 +2085,175 @@ KERNEL=="sda", TAG+="aaa" \\ + KERNEL=="sdb", TAG+="bbb" + TAGS=="foo", SYMLINK+="found" + TAGS=="aaa", SYMLINK+="bad" ++EOF ++ }, ++ { ++ desc => "multiple devices", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ exp_links => ["part-10"], ++ }, ++ ], ++ rules => < "multiple devices, same link name, positive prio", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ not_exp_links => ["partition"], ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7", "partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ not_exp_links => ["partition"], ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ not_exp_links => ["partition"], ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ not_exp_links => ["partition"], ++ exp_links => ["part-10"], ++ }, ++ ], ++ rules => < "multiple devices, same link name, negative prio", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ not_exp_links => ["partition"], ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7", "partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ not_exp_links => ["partition"], ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ not_exp_links => ["partition"], ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ not_exp_links => ["partition"], ++ exp_links => ["part-10"], ++ }, ++ ], ++ rules => < "multiple devices, same link name, positive prio, sleep", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ not_exp_links => ["partition"], ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7", "partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ not_exp_links => ["partition"], ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ not_exp_links => ["partition"], ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ not_exp_links => ["partition"], ++ exp_links => ["part-10"], ++ }, ++ ], ++ sleep_us => 10000, ++ rules => < +Date: Tue, 24 Apr 2018 22:24:43 +0200 +Subject: [PATCH] test/udev-test.pl: add repeat count + +for easier reproduction of sporadic test failures. + +(cherry picked from commit 2ab0a8d00bc48d3531e953d938db889d8a932d65) + +Related: #1642728 +--- + test/udev-test.pl | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index d964c664b6..8b1ab3c06c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2125,6 +2125,7 @@ EOF + }, + { + desc => "multiple devices, same link name, positive prio", ++ repeat => 100, + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +@@ -2635,6 +2636,7 @@ sub run_test { + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); + ++ REPEAT: + fork_and_run_udev("add", $rules, $sema); + + foreach my $dev (@devices) { +@@ -2653,6 +2655,9 @@ sub run_test { + } + + print "\n"; ++ if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) { ++ goto REPEAT; ++ } + + if (defined($rules->{option}) && $rules->{option} eq "clean") { + udev_setup(); diff --git a/SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch b/SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch new file mode 100644 index 0000000..02e407d --- /dev/null +++ b/SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch @@ -0,0 +1,101 @@ +From 6c3191e979165700f98903b76621c214186a110c Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Wed, 25 Apr 2018 09:54:26 +0200 +Subject: [PATCH] test/udev-test.pl: generator for large list of block devices + +Manually listing all devices in the test definition becomes cumbersome with +lots of devices. Add a function that scans on all block devices in +the test sysfs and generates a list of devices to test. + +(cherry picked from commit eb44d715ebee2fe11288433b99f8e1dc5fdac84a) + +Related: #1642728 +--- + test/udev-test.pl | 60 ++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 59 insertions(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 8b1ab3c06c..2866fdb77a 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -50,6 +50,50 @@ for (my $i = 1; $i < 10000; ++$i) { + } + $rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n"; + ++# Create a device list with all block devices under /sys ++# (except virtual devices and cd-roms) ++# the optional argument exp_func returns expected and non-expected ++# symlinks for the device. ++sub all_block_devs { ++ my ($exp_func) = @_; ++ my @devices; ++ ++ foreach my $bd (glob "$udev_sys/dev/block/*") { ++ my $tgt = readlink($bd); ++ my ($exp, $notexp) = (undef, undef); ++ ++ next if ($tgt =~ m!/virtual/! || $tgt =~ m!/sr[0-9]*$!); ++ ++ $tgt =~ s!^\.\./\.\.!!; ++ ($exp, $notexp) = $exp_func->($tgt) if defined($exp_func); ++ my $device = { ++ devpath => $tgt, ++ exp_links => $exp, ++ not_exp_links => $notexp, ++ }; ++ push(@devices, $device); ++ } ++ return \@devices; ++} ++ ++# This generator returns a suitable exp_func for use with ++# all_block_devs(). ++sub expect_for_some { ++ my ($pattern, $links, $donot) = @_; ++ my $_expect = sub { ++ my ($name) = @_; ++ ++ if ($name =~ /$pattern/) { ++ return ($links, undef); ++ } elsif ($donot) { ++ return (undef, $links); ++ } else { ++ return (undef, undef); ++ } ++ }; ++ return $_expect; ++} ++ + my @tests = ( + { + desc => "no rules", +@@ -2257,6 +2301,15 @@ SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partitio + KERNEL=="*7", OPTIONS+="link_priority=10" + EOF + }, ++ { ++ desc => 'all_block_devs', ++ generator => expect_for_some("\\/sda6\$", ["blockdev"]), ++ repeat => 10, ++ rules => <{devices}}; ++ my @devices; ++ ++ if (!defined $rules->{devices}) { ++ $rules->{devices} = all_block_devs($rules->{generator}); ++ } ++ @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); diff --git a/SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch b/SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch new file mode 100644 index 0000000..fbe9d4b --- /dev/null +++ b/SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch @@ -0,0 +1,29 @@ +From 453df9eb2bbfa34f3e4b78e917812f0ac6958010 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 26 Apr 2018 13:25:11 +0200 +Subject: [PATCH] test/udev-test.pl: suppress umount error message at startup + +umount emits an error message "no mount point specified" if the +tmpfs isn't mounted yet, which is the normal case. +Suppress that by redirecting stderr. + +(cherry picked from commit f1cb0860549e775be5f91237b5a3b97698dd14dd) + +Related: #1642728 +--- + test/udev-test.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 2866fdb77a..33a76ad292 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2405,7 +2405,7 @@ sub major_minor_test { + } + + sub udev_setup { +- system("umount", $udev_tmpfs); ++ system("umount \"$udev_tmpfs\" 2>/dev/null"); + rmdir($udev_tmpfs); + mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; + diff --git a/SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch b/SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch new file mode 100644 index 0000000..d1f61ca --- /dev/null +++ b/SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch @@ -0,0 +1,78 @@ +From e0cee95e0cc401ce120a1b56cdb7a8b9afbd6bcf Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 26 Apr 2018 14:07:27 +0200 +Subject: [PATCH] test/udev_test.pl: add "expected good" count + +Since 'test/udev-test.pl: count "good" results', we know how many +checks succeeded. Add an "expected good" count to make that number +more meaningful. + +(cherry picked from commit cbeb23d863d540408cd1fb274d78213f59639df2) + +Related: #1642728 +--- + test/udev-test.pl | 21 +++++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 33a76ad292..cf6ca6b80c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2338,6 +2338,7 @@ sub udev { + + my $error = 0; + my $good = 0; ++my $exp_good = 0; + + sub permissions_test { + my($rules, $uid, $gid, $mode) = @_; +@@ -2685,12 +2686,27 @@ sub run_test { + my ($rules, $number, $sema) = @_; + my $rc; + my @devices; ++ my $ntests; ++ my $cur_good = $good; ++ my $cur_error = $error; + + if (!defined $rules->{devices}) { + $rules->{devices} = all_block_devs($rules->{generator}); + } + @devices = @{$rules->{devices}}; ++ # For each device: exit status and devnode test for add & remove ++ $ntests += 4 * ($#devices + 1); + ++ foreach my $dev (@devices) { ++ $ntests += 2 * ($#{$dev->{exp_links}} + 1) ++ + ($#{$dev->{not_exp_links}} + 1) ++ + (defined $dev->{exp_perms} ? 1 : 0) ++ + (defined $dev->{exp_majorminor} ? 1 : 0); ++ } ++ if (defined $rules->{repeat}) { ++ $ntests *= $rules->{repeat}; ++ } ++ $exp_good += $ntests; + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); + +@@ -2712,10 +2728,11 @@ sub run_test { + check_remove($dev); + } + +- print "\n"; + if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) { + goto REPEAT; + } ++ printf "TEST $number: errors: %d good: %d/%d\n\n", $error-$cur_error, ++ $good-$cur_good, $ntests; + + if (defined($rules->{option}) && $rules->{option} eq "clean") { + udev_setup(); +@@ -2794,7 +2811,7 @@ if ($list[0]) { + } + + $sema->remove; +-print "$error errors occurred. $good good results.\n\n"; ++print "$error errors occurred. $good/$exp_good good results.\n\n"; + + cleanup(); + diff --git a/SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch b/SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch new file mode 100644 index 0000000..3e5c348 --- /dev/null +++ b/SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch @@ -0,0 +1,48 @@ +From 2e50a00f6930f1c65ca804b78f4a853e2ae2d2c0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 17 Nov 2020 17:13:31 +0100 +Subject: [PATCH] test/udev-test: gracefully exit when imports fail + +In Fedora rawhide various perl modules are now available as separate +packages that are not pulled in by dependencies. If we don't have some +package, skip the tests. + +This ugly code is apparently the way to do conditional imports: +https://www.cs.ait.ac.th/~on/O/oreilly/perl/cookbook/ch12_03.htm. + +(cherry picked from commit d40763838278246e2073d15ca927ee700e583afc) + +Related: #1642728 +--- + test/udev-test.pl | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index cf6ca6b80c..5b1e33504e 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -18,11 +18,19 @@ + + use warnings; + use strict; +-use POSIX qw(WIFEXITED WEXITSTATUS); +-use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); +-use IPC::Semaphore; +-use Time::HiRes qw(usleep); +-use Cwd qw(getcwd abs_path); ++ ++BEGIN { ++ my $EXIT_TEST_SKIP = 77; ++ ++ unless (eval "use POSIX qw(WIFEXITED WEXITSTATUS); ++ use Cwd qw(getcwd abs_path); ++ use IPC::Semaphore; ++ use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); ++ use Time::HiRes qw(usleep); 1") { ++ warn "Failed to import dependencies, skipping the test: $@"; ++ exit($EXIT_TEST_SKIP); ++ } ++} + + my $udev_bin = "./test-udev"; + my $valgrind = 0; diff --git a/SOURCES/0544-Revert-test-add-test-cases-for-empty-string-match-an.patch b/SOURCES/0544-Revert-test-add-test-cases-for-empty-string-match-an.patch new file mode 100644 index 0000000..fcfec2d --- /dev/null +++ b/SOURCES/0544-Revert-test-add-test-cases-for-empty-string-match-an.patch @@ -0,0 +1,123 @@ +From b05c8d2e10c773b9bcae17055be48a2291ca6fa6 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 2 Mar 2021 12:57:59 -0500 +Subject: [PATCH] Revert "test: add test cases for empty string match" and + "test: add test case for multi matches when use ||" + +This effectively reverts commits 03bc565e6e3249385c4e1ca0ae27670ca2ad9a41 +and 03b766cc937ffa4dcb7cfb25b2ac20d8a00cb6db. + +Resolves: #1931947 +--- + test/udev-test.pl | 98 ----------------------------------------------- + 1 file changed, 98 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 5b1e33504e..0612859cda 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1732,104 +1732,6 @@ KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1" + KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2" + KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right" + KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3" +-EOF +- }, +- { +- desc => "test multi matches 5", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < "test multi matches 6", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < "test multi matches 7", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < "test multi matches 8", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < "test multi matches 9", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < "test multi matches 10", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < "test multi matches 11", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_links => ["found"], +- not_exp_name => "bad", +- }], +- rules => < +Date: Tue, 24 Apr 2018 21:40:23 +0200 +Subject: [PATCH] test/sys-script.py: add missing DEVNAME entries to uevents + +Resolves: #1931947 +--- + test/sys-script.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/test/sys-script.py b/test/sys-script.py +index a51112603e..f4a847e356 100755 +--- a/test/sys-script.py ++++ b/test/sys-script.py +@@ -11677,6 +11677,7 @@ f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0: + f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/uevent', 0o644, b'''MAJOR=8 + MINOR=16 + DEVTYPE=disk ++DEVNAME=sdb + ''') + d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/queue', 0o755) + l('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/queue/bsg', '../../../bsg/7:0:0:0') +@@ -11709,6 +11710,7 @@ f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0: + f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/sdb1/uevent', 0o644, b'''MAJOR=8 + MINOR=17 + DEVTYPE=partition ++DEVNAME=sdb1 + ''') + d('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/sdb1/power', 0o755) + f('sys/devices/pci0000:00/0000:00:1d.7/usb5/5-1/5-1:1.0/host7/target7:0:0/7:0:0:0/block/sdb/sdb1/power/wakeup', 0o644, b'\n') +@@ -13150,6 +13152,7 @@ f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10 + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10/uevent', 0o644, b'''MAJOR=8 + MINOR=10 + DEVTYPE=partition ++DEVNAME=sda10 + ''') + d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10/power', 0o755) + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10/power/wakeup', 0o644, b'\n') +@@ -13163,6 +13166,7 @@ f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/ + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/uevent', 0o644, b'''MAJOR=8 + MINOR=9 + DEVTYPE=partition ++DEVNAME=sda9 + ''') + d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/holders', 0o755) + l('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9/holders/md0', '../../../../../../../../../virtual/block/md0') +@@ -13178,6 +13182,7 @@ f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/ + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/uevent', 0o644, b'''MAJOR=8 + MINOR=7 + DEVTYPE=partition ++DEVNAME=sda7 + ''') + d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/power', 0o755) + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7/power/wakeup', 0o644, b'\n') +@@ -13205,6 +13210,7 @@ f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/ + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/uevent', 0o644, b'''MAJOR=8 + MINOR=8 + DEVTYPE=partition ++DEVNAME=sda8 + ''') + d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/power', 0o755) + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8/power/wakeup', 0o644, b'\n') +@@ -13232,6 +13238,7 @@ f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/ + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/uevent', 0o644, b'''MAJOR=8 + MINOR=6 + DEVTYPE=partition ++DEVNAME=sda6 + ''') + d('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/power', 0o755) + f('sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6/power/wakeup', 0o644, b'\n') diff --git a/SOURCES/0546-sd-event-split-out-helper-functions-for-reshuffling-.patch b/SOURCES/0546-sd-event-split-out-helper-functions-for-reshuffling-.patch new file mode 100644 index 0000000..afc2d7e --- /dev/null +++ b/SOURCES/0546-sd-event-split-out-helper-functions-for-reshuffling-.patch @@ -0,0 +1,207 @@ +From 4ce10f8e41a85a56ad9b805442eb1149ece7c82a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Fri, 23 Oct 2020 18:29:27 +0200 +Subject: [PATCH] sd-event: split out helper functions for reshuffling prioqs + +We typically don't just reshuffle a single prioq at once, but always +two. Let's add two helper functions that do this, and reuse them +everywhere. + +(Note that this drops one minor optimization: +sd_event_source_set_time_accuracy() previously only reshuffled the +"latest" prioq, since changing the accuracy has no effect on the +earliest time of an event source, just the latest time an event source +can run. This optimization is removed to simplify things, given that +it's not really worth the effort as prioq_reshuffle() on properly +ordered prioqs has practically zero cost O(1)). + +(Slightly generalized, commented and split out of #17284 by Lennart) + +(cherry picked from commit e1951c16a8fbe5b0b9ecc08f4f835a806059d28f) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 96 ++++++++++++------------------ + 1 file changed, 38 insertions(+), 58 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 0d3bf5cbb6..47f99eb096 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -889,6 +889,33 @@ static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) + event_unmask_signal_data(e, d, sig); + } + ++static void event_source_pp_prioq_reshuffle(sd_event_source *s) { ++ assert(s); ++ ++ /* Reshuffles the pending + prepare prioqs. Called whenever the dispatch order changes, i.e. when ++ * they are enabled/disabled or marked pending and such. */ ++ ++ if (s->pending) ++ prioq_reshuffle(s->event->pending, s, &s->pending_index); ++ ++ if (s->prepare) ++ prioq_reshuffle(s->event->prepare, s, &s->prepare_index); ++} ++ ++static void event_source_time_prioq_reshuffle(sd_event_source *s) { ++ struct clock_data *d; ++ ++ assert(s); ++ assert(EVENT_SOURCE_IS_TIME(s->type)); ++ ++ /* Called whenever the event source's timer ordering properties changed, i.e. time, accuracy, ++ * pending, enable state. Makes sure the two prioq's are ordered properly again. */ ++ assert_se(d = event_get_clock_data(s->event, s->type)); ++ prioq_reshuffle(d->earliest, s, &s->time.earliest_index); ++ prioq_reshuffle(d->latest, s, &s->time.latest_index); ++ d->needs_rearm = true; ++} ++ + static void source_disconnect(sd_event_source *s) { + sd_event *event; + +@@ -1052,16 +1079,8 @@ static int source_set_pending(sd_event_source *s, bool b) { + } else + assert_se(prioq_remove(s->event->pending, s, &s->pending_index)); + +- if (EVENT_SOURCE_IS_TIME(s->type)) { +- struct clock_data *d; +- +- d = event_get_clock_data(s->event, s->type); +- assert(d); +- +- prioq_reshuffle(d->earliest, s, &s->time.earliest_index); +- prioq_reshuffle(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; +- } ++ if (EVENT_SOURCE_IS_TIME(s->type)) ++ event_source_time_prioq_reshuffle(s); + + if (s->type == SOURCE_SIGNAL && !b) { + struct signal_data *d; +@@ -2215,11 +2234,7 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) + } else + s->priority = priority; + +- if (s->pending) +- prioq_reshuffle(s->event->pending, s, &s->pending_index); +- +- if (s->prepare) +- prioq_reshuffle(s->event->prepare, s, &s->prepare_index); ++ event_source_pp_prioq_reshuffle(s); + + if (s->type == SOURCE_EXIT) + prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); +@@ -2280,18 +2295,10 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: +- case SOURCE_TIME_BOOTTIME_ALARM: { +- struct clock_data *d; +- ++ case SOURCE_TIME_BOOTTIME_ALARM: + s->enabled = m; +- d = event_get_clock_data(s->event, s->type); +- assert(d); +- +- prioq_reshuffle(d->earliest, s, &s->time.earliest_index); +- prioq_reshuffle(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; ++ event_source_time_prioq_reshuffle(s); + break; +- } + + case SOURCE_SIGNAL: + s->enabled = m; +@@ -2346,18 +2353,10 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: +- case SOURCE_TIME_BOOTTIME_ALARM: { +- struct clock_data *d; +- ++ case SOURCE_TIME_BOOTTIME_ALARM: + s->enabled = m; +- d = event_get_clock_data(s->event, s->type); +- assert(d); +- +- prioq_reshuffle(d->earliest, s, &s->time.earliest_index); +- prioq_reshuffle(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; ++ event_source_time_prioq_reshuffle(s); + break; +- } + + case SOURCE_SIGNAL: + +@@ -2405,11 +2404,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { + } + } + +- if (s->pending) +- prioq_reshuffle(s->event->pending, s, &s->pending_index); +- +- if (s->prepare) +- prioq_reshuffle(s->event->prepare, s, &s->prepare_index); ++ event_source_pp_prioq_reshuffle(s); + + return 0; + } +@@ -2425,7 +2420,6 @@ _public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { + } + + _public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { +- struct clock_data *d; + int r; + + assert_return(s, -EINVAL); +@@ -2439,13 +2433,7 @@ _public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { + + s->time.next = usec; + +- d = event_get_clock_data(s->event, s->type); +- assert(d); +- +- prioq_reshuffle(d->earliest, s, &s->time.earliest_index); +- prioq_reshuffle(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; +- ++ event_source_time_prioq_reshuffle(s); + return 0; + } + +@@ -2460,7 +2448,6 @@ _public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *use + } + + _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { +- struct clock_data *d; + int r; + + assert_return(s, -EINVAL); +@@ -2478,12 +2465,7 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec + + s->time.accuracy = usec; + +- d = event_get_clock_data(s->event, s->type); +- assert(d); +- +- prioq_reshuffle(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; +- ++ event_source_time_prioq_reshuffle(s); + return 0; + } + +@@ -2773,9 +2755,7 @@ static int process_timer( + if (r < 0) + return r; + +- prioq_reshuffle(d->earliest, s, &s->time.earliest_index); +- prioq_reshuffle(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; ++ event_source_time_prioq_reshuffle(s); + } + + return 0; diff --git a/SOURCES/0547-sd-event-split-out-enable-and-disable-codepaths-from.patch b/SOURCES/0547-sd-event-split-out-enable-and-disable-codepaths-from.patch new file mode 100644 index 0000000..040b83f --- /dev/null +++ b/SOURCES/0547-sd-event-split-out-enable-and-disable-codepaths-from.patch @@ -0,0 +1,306 @@ +From d7ad6ad123200f562081ff09f7bed3c6d969ac0a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 23 Oct 2020 21:21:58 +0200 +Subject: [PATCH] sd-event: split out enable and disable codepaths from + sd_event_source_set_enabled() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +So far half of sd_event_source_set_enabled() was doing enabling, the +other half was doing disabling. Let's split that into two separate +calls. + +(This also adds a new shortcut to sd_event_source_set_enabled(): if the +caller toggles between "ON" and "ONESHOT" we'll now shortcut this, since +the event source is already enabled in that case and shall remain +enabled.) + +This heavily borrows and is inspired from Michal Sekletár's #17284 +refactoring. + +(cherry picked from commit ddfde737b546c17e54182028153aa7f7e78804e3) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 227 +++++++++++++++-------------- + 1 file changed, 121 insertions(+), 106 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 47f99eb096..df5ed0dce8 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -2260,152 +2260,167 @@ _public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { + return 0; + } + +-_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { ++static int event_source_disable(sd_event_source *s) { + int r; + +- assert_return(s, -EINVAL); +- assert_return(IN_SET(m, SD_EVENT_OFF, SD_EVENT_ON, SD_EVENT_ONESHOT), -EINVAL); +- assert_return(!event_pid_changed(s->event), -ECHILD); ++ assert(s); ++ assert(s->enabled != SD_EVENT_OFF); + +- /* If we are dead anyway, we are fine with turning off +- * sources, but everything else needs to fail. */ +- if (s->event->state == SD_EVENT_FINISHED) +- return m == SD_EVENT_OFF ? 0 : -ESTALE; ++ /* Unset the pending flag when this event source is disabled */ ++ if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { ++ r = source_set_pending(s, false); ++ if (r < 0) ++ return r; ++ } + +- if (s->enabled == m) +- return 0; ++ s->enabled = SD_EVENT_OFF; + +- if (m == SD_EVENT_OFF) { ++ switch (s->type) { + +- /* Unset the pending flag when this event source is disabled */ +- if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { +- r = source_set_pending(s, false); +- if (r < 0) +- return r; +- } ++ case SOURCE_IO: ++ source_io_unregister(s); ++ break; + +- switch (s->type) { ++ case SOURCE_TIME_REALTIME: ++ case SOURCE_TIME_BOOTTIME: ++ case SOURCE_TIME_MONOTONIC: ++ case SOURCE_TIME_REALTIME_ALARM: ++ case SOURCE_TIME_BOOTTIME_ALARM: ++ event_source_time_prioq_reshuffle(s); ++ break; + +- case SOURCE_IO: +- source_io_unregister(s); +- s->enabled = m; +- break; ++ case SOURCE_SIGNAL: ++ event_gc_signal_data(s->event, &s->priority, s->signal.sig); ++ break; + +- case SOURCE_TIME_REALTIME: +- case SOURCE_TIME_BOOTTIME: +- case SOURCE_TIME_MONOTONIC: +- case SOURCE_TIME_REALTIME_ALARM: +- case SOURCE_TIME_BOOTTIME_ALARM: +- s->enabled = m; +- event_source_time_prioq_reshuffle(s); +- break; ++ case SOURCE_CHILD: ++ assert(s->event->n_enabled_child_sources > 0); ++ s->event->n_enabled_child_sources--; + +- case SOURCE_SIGNAL: +- s->enabled = m; ++ event_gc_signal_data(s->event, &s->priority, SIGCHLD); ++ break; + +- event_gc_signal_data(s->event, &s->priority, s->signal.sig); +- break; ++ case SOURCE_EXIT: ++ prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); ++ break; + +- case SOURCE_CHILD: +- s->enabled = m; ++ case SOURCE_DEFER: ++ case SOURCE_POST: ++ case SOURCE_INOTIFY: ++ break; + +- assert(s->event->n_enabled_child_sources > 0); +- s->event->n_enabled_child_sources--; ++ default: ++ assert_not_reached("Wut? I shouldn't exist."); ++ } + +- event_gc_signal_data(s->event, &s->priority, SIGCHLD); +- break; ++ return 0; ++} + +- case SOURCE_EXIT: +- s->enabled = m; +- prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); +- break; ++static int event_source_enable(sd_event_source *s, int m) { ++ int r; + +- case SOURCE_DEFER: +- case SOURCE_POST: +- case SOURCE_INOTIFY: +- s->enabled = m; +- break; ++ assert(s); ++ assert(IN_SET(m, SD_EVENT_ON, SD_EVENT_ONESHOT)); ++ assert(s->enabled == SD_EVENT_OFF); + +- default: +- assert_not_reached("Wut? I shouldn't exist."); +- } ++ /* Unset the pending flag when this event source is enabled */ ++ if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { ++ r = source_set_pending(s, false); ++ if (r < 0) ++ return r; ++ } + +- } else { ++ s->enabled = m; + +- /* Unset the pending flag when this event source is enabled */ +- if (s->enabled == SD_EVENT_OFF && !IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { +- r = source_set_pending(s, false); +- if (r < 0) +- return r; ++ switch (s->type) { ++ ++ case SOURCE_IO: ++ r = source_io_register(s, m, s->io.events); ++ if (r < 0) { ++ s->enabled = SD_EVENT_OFF; ++ return r; + } + +- switch (s->type) { ++ break; + +- case SOURCE_IO: +- r = source_io_register(s, m, s->io.events); +- if (r < 0) +- return r; ++ case SOURCE_TIME_REALTIME: ++ case SOURCE_TIME_BOOTTIME: ++ case SOURCE_TIME_MONOTONIC: ++ case SOURCE_TIME_REALTIME_ALARM: ++ case SOURCE_TIME_BOOTTIME_ALARM: ++ event_source_time_prioq_reshuffle(s); ++ break; + +- s->enabled = m; +- break; ++ case SOURCE_SIGNAL: ++ r = event_make_signal_data(s->event, s->signal.sig, NULL); ++ if (r < 0) { ++ s->enabled = SD_EVENT_OFF; ++ event_gc_signal_data(s->event, &s->priority, s->signal.sig); ++ return r; ++ } + +- case SOURCE_TIME_REALTIME: +- case SOURCE_TIME_BOOTTIME: +- case SOURCE_TIME_MONOTONIC: +- case SOURCE_TIME_REALTIME_ALARM: +- case SOURCE_TIME_BOOTTIME_ALARM: +- s->enabled = m; +- event_source_time_prioq_reshuffle(s); +- break; ++ break; + +- case SOURCE_SIGNAL: ++ case SOURCE_CHILD: ++ s->event->n_enabled_child_sources++; + +- s->enabled = m; ++ r = event_make_signal_data(s->event, SIGCHLD, NULL); ++ if (r < 0) { ++ s->enabled = SD_EVENT_OFF; ++ s->event->n_enabled_child_sources--; ++ event_gc_signal_data(s->event, &s->priority, SIGCHLD); ++ return r; ++ } + +- r = event_make_signal_data(s->event, s->signal.sig, NULL); +- if (r < 0) { +- s->enabled = SD_EVENT_OFF; +- event_gc_signal_data(s->event, &s->priority, s->signal.sig); +- return r; +- } + +- break; ++ break; + +- case SOURCE_CHILD: ++ case SOURCE_EXIT: ++ prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); ++ break; + +- if (s->enabled == SD_EVENT_OFF) +- s->event->n_enabled_child_sources++; ++ case SOURCE_DEFER: ++ case SOURCE_POST: ++ case SOURCE_INOTIFY: ++ break; + +- s->enabled = m; ++ default: ++ assert_not_reached("Wut? I shouldn't exist."); ++ } + +- r = event_make_signal_data(s->event, SIGCHLD, NULL); +- if (r < 0) { +- s->enabled = SD_EVENT_OFF; +- s->event->n_enabled_child_sources--; +- event_gc_signal_data(s->event, &s->priority, SIGCHLD); +- return r; +- } ++ return 0; ++} + +- break; ++_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { ++ int r; + +- case SOURCE_EXIT: +- s->enabled = m; +- prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); +- break; ++ assert_return(s, -EINVAL); ++ assert_return(IN_SET(m, SD_EVENT_OFF, SD_EVENT_ON, SD_EVENT_ONESHOT), -EINVAL); ++ assert_return(!event_pid_changed(s->event), -ECHILD); + +- case SOURCE_DEFER: +- case SOURCE_POST: +- case SOURCE_INOTIFY: +- s->enabled = m; +- break; ++ /* If we are dead anyway, we are fine with turning off sources, but everything else needs to fail. */ ++ if (s->event->state == SD_EVENT_FINISHED) ++ return m == SD_EVENT_OFF ? 0 : -ESTALE; + +- default: +- assert_not_reached("Wut? I shouldn't exist."); ++ if (s->enabled == m) /* No change? */ ++ return 0; ++ ++ if (m == SD_EVENT_OFF) ++ r = event_source_disable(s); ++ else { ++ if (s->enabled != SD_EVENT_OFF) { ++ /* Switching from "on" to "oneshot" or back? If that's the case, we can take a shortcut, the ++ * event source is already enabled after all. */ ++ s->enabled = m; ++ return 0; + } ++ ++ r = event_source_enable(s, m); + } ++ if (r < 0) ++ return r; + + event_source_pp_prioq_reshuffle(s); +- + return 0; + } + diff --git a/SOURCES/0548-sd-event-mention-that-two-debug-logged-events-are-ig.patch b/SOURCES/0548-sd-event-mention-that-two-debug-logged-events-are-ig.patch new file mode 100644 index 0000000..6ea67ca --- /dev/null +++ b/SOURCES/0548-sd-event-mention-that-two-debug-logged-events-are-ig.patch @@ -0,0 +1,37 @@ +From e8904b5b7957bfc9328f1ab8b6851c7b0d8920c9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 11:39:40 +0100 +Subject: [PATCH] sd-event: mention that two debug logged events are ignored + +(cherry picked from commit f80a5d6a86dc2346f406ee086ba179879afaab70) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index df5ed0dce8..0f507f18d8 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -596,8 +596,6 @@ static bool event_pid_changed(sd_event *e) { + } + + static void source_io_unregister(sd_event_source *s) { +- int r; +- + assert(s); + assert(s->type == SOURCE_IO); + +@@ -607,9 +605,8 @@ static void source_io_unregister(sd_event_source *s) { + if (!s->io.registered) + return; + +- r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL); +- if (r < 0) +- log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m", ++ if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL) < 0) ++ log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m", + strna(s->description), event_source_type_to_string(s->type)); + + s->io.registered = false; diff --git a/SOURCES/0549-sd-event-split-clock-data-allocation-out-of-sd_event.patch b/SOURCES/0549-sd-event-split-clock-data-allocation-out-of-sd_event.patch new file mode 100644 index 0000000..6e512af --- /dev/null +++ b/SOURCES/0549-sd-event-split-clock-data-allocation-out-of-sd_event.patch @@ -0,0 +1,71 @@ +From 6cc0022115afbac9ac66c456b140601d90271687 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 11:40:24 +0100 +Subject: [PATCH] sd-event: split clock data allocation out of + sd_event_add_time() + +Just some simple refactoring, that will make things easier for us later. +But it looks better this way even without the later function reuse. + +(cherry picked from commit 41c63f36c3352af8bebf03b6181f5d866431d0af) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 34 ++++++++++++++++++++---------- + 1 file changed, 23 insertions(+), 11 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 0f507f18d8..dc78dc7291 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -1232,6 +1232,28 @@ static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata) + return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); + } + ++static int setup_clock_data(sd_event *e, struct clock_data *d, clockid_t clock) { ++ int r; ++ ++ assert(d); ++ ++ if (d->fd < 0) { ++ r = event_setup_timer_fd(e, d, clock); ++ if (r < 0) ++ return r; ++ } ++ ++ r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); ++ if (r < 0) ++ return r; ++ ++ r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare); ++ if (r < 0) ++ return r; ++ ++ return 0; ++} ++ + _public_ int sd_event_add_time( + sd_event *e, + sd_event_source **ret, +@@ -1265,20 +1287,10 @@ _public_ int sd_event_add_time( + d = event_get_clock_data(e, type); + assert(d); + +- r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); +- if (r < 0) +- return r; +- +- r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare); ++ r = setup_clock_data(e, d, clock); + if (r < 0) + return r; + +- if (d->fd < 0) { +- r = event_setup_timer_fd(e, d, clock); +- if (r < 0) +- return r; +- } +- + s = source_new(e, !ret, type); + if (!s) + return -ENOMEM; diff --git a/SOURCES/0550-sd-event-split-out-code-to-add-remove-timer-event-so.patch b/SOURCES/0550-sd-event-split-out-code-to-add-remove-timer-event-so.patch new file mode 100644 index 0000000..0333f0d --- /dev/null +++ b/SOURCES/0550-sd-event-split-out-code-to-add-remove-timer-event-so.patch @@ -0,0 +1,112 @@ +From 88b2618e4de850060a1c5c22b049e6de0578fbb5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 15:25:35 +0100 +Subject: [PATCH] sd-event: split out code to add/remove timer event sources to + earliest/latest prioq + +Just some refactoring that makes code prettier, and will come handy +later, because we can reuse these functions at more places. + +(cherry picked from commit 1e45e3fecc303e7ae9946220c742f69675e99c34) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 57 +++++++++++++++++++++--------- + 1 file changed, 41 insertions(+), 16 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index dc78dc7291..de9bb02059 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -913,6 +913,19 @@ static void event_source_time_prioq_reshuffle(sd_event_source *s) { + d->needs_rearm = true; + } + ++static void event_source_time_prioq_remove( ++ sd_event_source *s, ++ struct clock_data *d) { ++ ++ assert(s); ++ assert(d); ++ ++ prioq_remove(d->earliest, s, &s->time.earliest_index); ++ prioq_remove(d->latest, s, &s->time.latest_index); ++ s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL; ++ d->needs_rearm = true; ++} ++ + static void source_disconnect(sd_event_source *s) { + sd_event *event; + +@@ -937,13 +950,8 @@ static void source_disconnect(sd_event_source *s) { + case SOURCE_TIME_REALTIME_ALARM: + case SOURCE_TIME_BOOTTIME_ALARM: { + struct clock_data *d; +- +- d = event_get_clock_data(s->event, s->type); +- assert(d); +- +- prioq_remove(d->earliest, s, &s->time.earliest_index); +- prioq_remove(d->latest, s, &s->time.latest_index); +- d->needs_rearm = true; ++ assert_se(d = event_get_clock_data(s->event, s->type)); ++ event_source_time_prioq_remove(s, d); + break; + } + +@@ -1254,6 +1262,30 @@ static int setup_clock_data(sd_event *e, struct clock_data *d, clockid_t clock) + return 0; + } + ++static int event_source_time_prioq_put( ++ sd_event_source *s, ++ struct clock_data *d) { ++ ++ int r; ++ ++ assert(s); ++ assert(d); ++ ++ r = prioq_put(d->earliest, s, &s->time.earliest_index); ++ if (r < 0) ++ return r; ++ ++ r = prioq_put(d->latest, s, &s->time.latest_index); ++ if (r < 0) { ++ assert_se(prioq_remove(d->earliest, s, &s->time.earliest_index) > 0); ++ s->time.earliest_index = PRIOQ_IDX_NULL; ++ return r; ++ } ++ ++ d->needs_rearm = true; ++ return 0; ++} ++ + _public_ int sd_event_add_time( + sd_event *e, + sd_event_source **ret, +@@ -1284,8 +1316,7 @@ _public_ int sd_event_add_time( + if (!callback) + callback = time_exit_callback; + +- d = event_get_clock_data(e, type); +- assert(d); ++ assert_se(d = event_get_clock_data(e, type)); + + r = setup_clock_data(e, d, clock); + if (r < 0) +@@ -1302,13 +1333,7 @@ _public_ int sd_event_add_time( + s->userdata = userdata; + s->enabled = SD_EVENT_ONESHOT; + +- d->needs_rearm = true; +- +- r = prioq_put(d->earliest, s, &s->time.earliest_index); +- if (r < 0) +- goto fail; +- +- r = prioq_put(d->latest, s, &s->time.latest_index); ++ r = event_source_time_prioq_put(s, d); + if (r < 0) + goto fail; + diff --git a/SOURCES/0551-sd-event-fix-delays-assert-brain-o-17790.patch b/SOURCES/0551-sd-event-fix-delays-assert-brain-o-17790.patch new file mode 100644 index 0000000..64afc46 --- /dev/null +++ b/SOURCES/0551-sd-event-fix-delays-assert-brain-o-17790.patch @@ -0,0 +1,29 @@ +From 1aae57f763cacbdeb10647c627cf307f79ad00ca Mon Sep 17 00:00:00 2001 +From: Vito Caputo +Date: Tue, 1 Dec 2020 00:26:54 -0800 +Subject: [PATCH] sd-event: fix delays assert brain-o (#17790) + +s/sizeof/ELEMENTSOF/ + +Bug introduced in 34b87517749caa4142b19eb3c63bdf349fafbc49. + +(cherry picked from commit cb9d621ebbfa30bbd620c17e143daeb0d78c12f0) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index de9bb02059..9d42157206 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -3586,7 +3586,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { + this_run = now(CLOCK_MONOTONIC); + + l = u64log2(this_run - e->last_run); +- assert(l < sizeof(e->delays)); ++ assert(l < ELEMENTSOF(e->delays)); + e->delays[l]++; + + if (this_run - e->last_log >= 5*USEC_PER_SEC) { diff --git a/SOURCES/0552-sd-event-let-s-suffix-last_run-last_log-with-_usec.patch b/SOURCES/0552-sd-event-let-s-suffix-last_run-last_log-with-_usec.patch new file mode 100644 index 0000000..1393d01 --- /dev/null +++ b/SOURCES/0552-sd-event-let-s-suffix-last_run-last_log-with-_usec.patch @@ -0,0 +1,60 @@ +From 5b5a590c5a8bbaebbecf4ab4797334a09a870287 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 15:33:50 +0100 +Subject: [PATCH] sd-event: let's suffix last_run/last_log with "_usec" + +Otherwise it's a bit confusing what this is about: two timestamps. + +(cherry picked from commit e6a7bee538f6638c2d5ca2afc66bf47cba3f075c) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 9d42157206..88641879cc 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -301,7 +301,7 @@ struct sd_event { + + LIST_HEAD(sd_event_source, sources); + +- usec_t last_run, last_log; ++ usec_t last_run_usec, last_log_usec; + unsigned delays[sizeof(usec_t) * 8]; + }; + +@@ -3579,19 +3579,19 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); + +- if (e->profile_delays && e->last_run) { ++ if (e->profile_delays && e->last_run_usec != 0) { + usec_t this_run; + unsigned l; + + this_run = now(CLOCK_MONOTONIC); + +- l = u64log2(this_run - e->last_run); ++ l = u64log2(this_run - e->last_run_usec); + assert(l < ELEMENTSOF(e->delays)); + e->delays[l]++; + +- if (this_run - e->last_log >= 5*USEC_PER_SEC) { ++ if (this_run - e->last_log_usec >= 5*USEC_PER_SEC) { + event_log_delays(e); +- e->last_log = this_run; ++ e->last_log_usec = this_run; + } + } + +@@ -3601,7 +3601,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { + r = sd_event_wait(e, timeout); + + if (e->profile_delays) +- e->last_run = now(CLOCK_MONOTONIC); ++ e->last_run_usec = now(CLOCK_MONOTONIC); + + if (r > 0) { + /* There's something now, then let's dispatch it */ diff --git a/SOURCES/0553-sd-event-refuse-running-default-event-loops-in-any-o.patch b/SOURCES/0553-sd-event-refuse-running-default-event-loops-in-any-o.patch new file mode 100644 index 0000000..95ad16f --- /dev/null +++ b/SOURCES/0553-sd-event-refuse-running-default-event-loops-in-any-o.patch @@ -0,0 +1,42 @@ +From 4c5fdbde7e745126f31542a70b45cc4faec094d2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 30 Oct 2019 20:26:50 +0100 +Subject: [PATCH] sd-event: refuse running default event loops in any other + thread than the one they are default for + +(cherry picked from commit e544601536ac13a288d7476f4400c7b0f22b7ea1) + +Related: #1819868 +--- + TODO | 1 - + src/libsystemd/sd-event/sd-event.c | 5 +++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/TODO b/TODO +index 8f78000089..3100e067d6 100644 +--- a/TODO ++++ b/TODO +@@ -581,7 +581,6 @@ Features: + - allow multiple signal handlers per signal? + - document chaining of signal handler for SIGCHLD and child handlers + - define more intervals where we will shift wakeup intervals around in, 1h, 6h, 24h, ... +- - generate a failure of a default event loop is executed out-of-thread + - maybe add support for inotify events (which we can do safely now, with O_PATH) + + * investigate endianness issues of UUID vs. GUID +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 88641879cc..537a0b81d4 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -3360,6 +3360,11 @@ _public_ int sd_event_prepare(sd_event *e) { + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); + ++ /* Let's check that if we are a default event loop we are executed in the correct thread. We only do ++ * this check here once, since gettid() is typically not cached, and thus want to minimize ++ * syscalls */ ++ assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO); ++ + if (e->exit_requested) + goto pending; + diff --git a/SOURCES/0554-sd-event-ref-event-loop-while-in-sd_event_prepare-ot.patch b/SOURCES/0554-sd-event-ref-event-loop-while-in-sd_event_prepare-ot.patch new file mode 100644 index 0000000..4824f09 --- /dev/null +++ b/SOURCES/0554-sd-event-ref-event-loop-while-in-sd_event_prepare-ot.patch @@ -0,0 +1,92 @@ +From 441684c6c961edec1391562fb2f48ff997a6169e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 15:38:00 +0100 +Subject: [PATCH] sd-event: ref event loop while in sd_event_prepare() ot + sd_event_run() + +sd_event_prepare() invokes callbacks that might drop the last user ref +on our event loop. Let's make sure we keep an explicit ref around it, so +that we won't end up with an invalid pointer. Similar in sd_event_run(). + +Basically, any function that is publically callable that might end up +invoking callbacks should ref the relevant objects to be protected +against callbacks destroying these objects while we still want to access +them. We did this correctly in sd_event_dispatch() and sd_event_loop(), +but these are not the only ones which are callable from the outside. + +(cherry picked from commit f814c871e65df8552a055dd887bc94b074037833) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 537a0b81d4..be1e6e5f53 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -3256,7 +3256,6 @@ static int event_prepare(sd_event *e) { + + static int dispatch_exit(sd_event *e) { + sd_event_source *p; +- _cleanup_(sd_event_unrefp) sd_event *ref = NULL; + int r; + + assert(e); +@@ -3267,7 +3266,7 @@ static int dispatch_exit(sd_event *e) { + return 0; + } + +- ref = sd_event_ref(e); ++ _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e); + e->iteration++; + e->state = SD_EVENT_EXITING; + r = source_dispatch(p); +@@ -3365,6 +3364,9 @@ _public_ int sd_event_prepare(sd_event *e) { + * syscalls */ + assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO); + ++ /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */ ++ _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e); ++ + if (e->exit_requested) + goto pending; + +@@ -3549,9 +3551,8 @@ _public_ int sd_event_dispatch(sd_event *e) { + + p = event_next_pending(e); + if (p) { +- _cleanup_(sd_event_unrefp) sd_event *ref = NULL; ++ _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e); + +- ref = sd_event_ref(e); + e->state = SD_EVENT_RUNNING; + r = source_dispatch(p); + e->state = SD_EVENT_INITIAL; +@@ -3600,6 +3601,9 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { + } + } + ++ /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */ ++ _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e); ++ + r = sd_event_prepare(e); + if (r == 0) + /* There was nothing? Then wait... */ +@@ -3621,7 +3625,6 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { + } + + _public_ int sd_event_loop(sd_event *e) { +- _cleanup_(sd_event_unrefp) sd_event *ref = NULL; + int r; + + assert_return(e, -EINVAL); +@@ -3629,7 +3632,7 @@ _public_ int sd_event_loop(sd_event *e) { + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); + +- ref = sd_event_ref(e); ++ _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL; + + while (e->state != SD_EVENT_FINISHED) { + r = sd_event_run(e, (uint64_t) -1); diff --git a/SOURCES/0555-sd-event-follow-coding-style-with-naming-return-para.patch b/SOURCES/0555-sd-event-follow-coding-style-with-naming-return-para.patch new file mode 100644 index 0000000..ab585e0 --- /dev/null +++ b/SOURCES/0555-sd-event-follow-coding-style-with-naming-return-para.patch @@ -0,0 +1,35 @@ +From 846b1dd75e626ad2e2483673eea65774edea9016 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 17:47:16 +0100 +Subject: [PATCH] sd-event: follow coding style with naming return parameter + +(cherry picked from commit cad143a8f26976a23e634d5e1ecfb7d7ba75c3bf) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index be1e6e5f53..739296abcf 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -2285,13 +2285,14 @@ fail: + return r; + } + +-_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { ++_public_ int sd_event_source_get_enabled(sd_event_source *s, int *ret) { + assert_return(s, -EINVAL); +- assert_return(m, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + +- *m = s->enabled; +- return 0; ++ if (ret) ++ *ret = s->enabled; ++ ++ return s->enabled != SD_EVENT_OFF; + } + + static int event_source_disable(sd_event_source *s) { diff --git a/SOURCES/0556-sd-event-remove-earliest_index-latest_index-into-com.patch b/SOURCES/0556-sd-event-remove-earliest_index-latest_index-into-com.patch new file mode 100644 index 0000000..7fb9b47 --- /dev/null +++ b/SOURCES/0556-sd-event-remove-earliest_index-latest_index-into-com.patch @@ -0,0 +1,98 @@ +From 97f599bf57fdaee688ae5750e9b2b2587e2b597a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 17:49:27 +0100 +Subject: [PATCH] sd-event: remove earliest_index/latest_index into common part + of event source objects + +So far we used these fields to organize the earliest/latest timer event +priority queue. In a follow-up commit we want to introduce ratelimiting +to event sources, at which point we want any kind of event source to be +able to trigger time wakeups, and hence they all need to be included in +the earliest/latest prioqs. Thus, in preparation let's make this +generic. + +No change in behaviour, just some shifting around of struct members from +the type-specific to the generic part. + +(cherry picked from commit f41315fceb5208c496145cda2d6c865a5458ce44) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 739296abcf..34b42c298f 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -107,6 +107,9 @@ struct sd_event_source { + + LIST_FIELDS(sd_event_source, sources); + ++ unsigned earliest_index; ++ unsigned latest_index; ++ + union { + struct { + sd_event_io_handler_t callback; +@@ -119,8 +122,6 @@ struct sd_event_source { + struct { + sd_event_time_handler_t callback; + usec_t next, accuracy; +- unsigned earliest_index; +- unsigned latest_index; + } time; + struct { + sd_event_signal_handler_t callback; +@@ -908,8 +909,8 @@ static void event_source_time_prioq_reshuffle(sd_event_source *s) { + /* Called whenever the event source's timer ordering properties changed, i.e. time, accuracy, + * pending, enable state. Makes sure the two prioq's are ordered properly again. */ + assert_se(d = event_get_clock_data(s->event, s->type)); +- prioq_reshuffle(d->earliest, s, &s->time.earliest_index); +- prioq_reshuffle(d->latest, s, &s->time.latest_index); ++ prioq_reshuffle(d->earliest, s, &s->earliest_index); ++ prioq_reshuffle(d->latest, s, &s->latest_index); + d->needs_rearm = true; + } + +@@ -920,9 +921,9 @@ static void event_source_time_prioq_remove( + assert(s); + assert(d); + +- prioq_remove(d->earliest, s, &s->time.earliest_index); +- prioq_remove(d->latest, s, &s->time.latest_index); +- s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL; ++ prioq_remove(d->earliest, s, &s->earliest_index); ++ prioq_remove(d->latest, s, &s->latest_index); ++ s->earliest_index = s->latest_index = PRIOQ_IDX_NULL; + d->needs_rearm = true; + } + +@@ -1271,14 +1272,14 @@ static int event_source_time_prioq_put( + assert(s); + assert(d); + +- r = prioq_put(d->earliest, s, &s->time.earliest_index); ++ r = prioq_put(d->earliest, s, &s->earliest_index); + if (r < 0) + return r; + +- r = prioq_put(d->latest, s, &s->time.latest_index); ++ r = prioq_put(d->latest, s, &s->latest_index); + if (r < 0) { +- assert_se(prioq_remove(d->earliest, s, &s->time.earliest_index) > 0); +- s->time.earliest_index = PRIOQ_IDX_NULL; ++ assert_se(prioq_remove(d->earliest, s, &s->earliest_index) > 0); ++ s->earliest_index = PRIOQ_IDX_NULL; + return r; + } + +@@ -1329,7 +1330,7 @@ _public_ int sd_event_add_time( + s->time.next = usec; + s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy; + s->time.callback = callback; +- s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL; ++ s->earliest_index = s->latest_index = PRIOQ_IDX_NULL; + s->userdata = userdata; + s->enabled = SD_EVENT_ONESHOT; + diff --git a/SOURCES/0557-sd-event-update-state-at-the-end-in-event_source_ena.patch b/SOURCES/0557-sd-event-update-state-at-the-end-in-event_source_ena.patch new file mode 100644 index 0000000..e0a2145 --- /dev/null +++ b/SOURCES/0557-sd-event-update-state-at-the-end-in-event_source_ena.patch @@ -0,0 +1,116 @@ +From deb9e6ad3a1d7cfbc3b53d1e74cda6ae398a90fd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 10 Nov 2020 10:38:37 +0100 +Subject: [PATCH] sd-event: update state at the end in event_source_enable + +Coverity in CID#1435966 was complaining that s->enabled is not "restored" in +all cases. But the code was actually correct, since it should only be +"restored" in the error paths. But let's still make this prettier by not setting +the state before all operations that may fail are done. + +We need to set .enabled for the prioq reshuffling operations, so move those down. + +No functional change intended. + +(cherry picked from commit d2eafe61ca07f8300dc741a0491a914213fa2b6b) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 51 +++++++++++++++++------------- + 1 file changed, 29 insertions(+), 22 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 34b42c298f..0cfba8fb39 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -2352,11 +2352,11 @@ static int event_source_disable(sd_event_source *s) { + return 0; + } + +-static int event_source_enable(sd_event_source *s, int m) { ++static int event_source_enable(sd_event_source *s, int enable) { + int r; + + assert(s); +- assert(IN_SET(m, SD_EVENT_ON, SD_EVENT_ONESHOT)); ++ assert(IN_SET(enable, SD_EVENT_ON, SD_EVENT_ONESHOT)); + assert(s->enabled == SD_EVENT_OFF); + + /* Unset the pending flag when this event source is enabled */ +@@ -2366,31 +2366,16 @@ static int event_source_enable(sd_event_source *s, int m) { + return r; + } + +- s->enabled = m; +- + switch (s->type) { +- + case SOURCE_IO: +- r = source_io_register(s, m, s->io.events); +- if (r < 0) { +- s->enabled = SD_EVENT_OFF; ++ r = source_io_register(s, enable, s->io.events); ++ if (r < 0) + return r; +- } +- +- break; +- +- case SOURCE_TIME_REALTIME: +- case SOURCE_TIME_BOOTTIME: +- case SOURCE_TIME_MONOTONIC: +- case SOURCE_TIME_REALTIME_ALARM: +- case SOURCE_TIME_BOOTTIME_ALARM: +- event_source_time_prioq_reshuffle(s); + break; + + case SOURCE_SIGNAL: + r = event_make_signal_data(s->event, s->signal.sig, NULL); + if (r < 0) { +- s->enabled = SD_EVENT_OFF; + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + return r; + } +@@ -2411,10 +2396,12 @@ static int event_source_enable(sd_event_source *s, int m) { + + break; + ++ case SOURCE_TIME_REALTIME: ++ case SOURCE_TIME_BOOTTIME: ++ case SOURCE_TIME_MONOTONIC: ++ case SOURCE_TIME_REALTIME_ALARM: ++ case SOURCE_TIME_BOOTTIME_ALARM: + case SOURCE_EXIT: +- prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); +- break; +- + case SOURCE_DEFER: + case SOURCE_POST: + case SOURCE_INOTIFY: +@@ -2424,6 +2411,26 @@ static int event_source_enable(sd_event_source *s, int m) { + assert_not_reached("Wut? I shouldn't exist."); + } + ++ s->enabled = enable; ++ ++ /* Non-failing operations below */ ++ switch (s->type) { ++ case SOURCE_TIME_REALTIME: ++ case SOURCE_TIME_BOOTTIME: ++ case SOURCE_TIME_MONOTONIC: ++ case SOURCE_TIME_REALTIME_ALARM: ++ case SOURCE_TIME_BOOTTIME_ALARM: ++ event_source_time_prioq_reshuffle(s); ++ break; ++ ++ case SOURCE_EXIT: ++ prioq_reshuffle(s->event->exit, s, &s->exit.prioq_index); ++ break; ++ ++ default: ++ break; ++ } ++ + return 0; + } + diff --git a/SOURCES/0558-sd-event-increase-n_enabled_child_sources-just-once.patch b/SOURCES/0558-sd-event-increase-n_enabled_child_sources-just-once.patch new file mode 100644 index 0000000..2a41752 --- /dev/null +++ b/SOURCES/0558-sd-event-increase-n_enabled_child_sources-just-once.patch @@ -0,0 +1,36 @@ +From 188465c472996b426a1f22a9fc46d031b722c3b4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 10 Nov 2020 12:57:34 +0100 +Subject: [PATCH] sd-event: increase n_enabled_child_sources just once + +Neither source_child_pidfd_register() nor event_make_signal_data() look at +n_enabled_child_sources. + +(cherry picked from commit ac9f2640cb9c107b43f47bba7e068d3b92b5337b) + +Related: #1819868 +--- + src/libsystemd/sd-event/sd-event.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 0cfba8fb39..d18ce28a92 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -2383,8 +2383,6 @@ static int event_source_enable(sd_event_source *s, int enable) { + break; + + case SOURCE_CHILD: +- s->event->n_enabled_child_sources++; +- + r = event_make_signal_data(s->event, SIGCHLD, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; +@@ -2393,6 +2391,7 @@ static int event_source_enable(sd_event_source *s, int enable) { + return r; + } + ++ s->event->n_enabled_child_sources++; + + break; + diff --git a/SOURCES/0559-sd-event-add-ability-to-ratelimit-event-sources.patch b/SOURCES/0559-sd-event-add-ability-to-ratelimit-event-sources.patch new file mode 100644 index 0000000..b8c20d0 --- /dev/null +++ b/SOURCES/0559-sd-event-add-ability-to-ratelimit-event-sources.patch @@ -0,0 +1,858 @@ +From 395eb7753a9772f505102fbbe3ba3261b57abbe9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 23 Nov 2020 18:02:40 +0100 +Subject: [PATCH] sd-event: add ability to ratelimit event sources + +Let's a concept of "rate limiting" to event sources: if specific event +sources fire too often in some time interval temporarily take them +offline, and take them back online once the interval passed. + +This is a simple scheme of avoiding starvation of event sources if some +event source fires too often. + +This introduces the new conceptual states of "offline" and "online" for +event sources: an event source is "online" only when enabled *and* not +ratelimited, and offline in all other cases. An event source that is +online hence has its fds registered in the epoll, its signals in the +signalfd and so on. + +(cherry picked from commit b6d5481b3d9f7c9b1198ab54b54326ec73e855bf) + +Related: #1819868 +--- + src/basic/ratelimit.h | 8 + + src/libsystemd/libsystemd.sym | 7 + + src/libsystemd/sd-event/sd-event.c | 433 +++++++++++++++++++++++------ + src/systemd/sd-event.h | 3 + + 4 files changed, 369 insertions(+), 82 deletions(-) + +diff --git a/src/basic/ratelimit.h b/src/basic/ratelimit.h +index de91def28d..0012b49935 100644 +--- a/src/basic/ratelimit.h ++++ b/src/basic/ratelimit.h +@@ -38,3 +38,11 @@ typedef struct RateLimit { + } while (false) + + bool ratelimit_below(RateLimit *r); ++ ++static inline void ratelimit_reset(RateLimit *rl) { ++ rl->num = rl->begin = 0; ++} ++ ++static inline bool ratelimit_configured(RateLimit *rl) { ++ return rl->interval > 0 && rl->burst > 0; ++} +diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym +index 778e88a16c..149d2e7b82 100644 +--- a/src/libsystemd/libsystemd.sym ++++ b/src/libsystemd/libsystemd.sym +@@ -572,3 +572,10 @@ global: + sd_bus_enqueue_for_read; + sd_event_source_disable_unref; + } LIBSYSTEMD_238; ++ ++LIBSYSTEMD_248 { ++global: ++ sd_event_source_set_ratelimit; ++ sd_event_source_get_ratelimit; ++ sd_event_source_is_ratelimited; ++} LIBSYSTEMD_239; +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index d18ce28a92..be912d94e3 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -19,6 +19,7 @@ + #include "missing.h" + #include "prioq.h" + #include "process-util.h" ++#include "ratelimit.h" + #include "set.h" + #include "signal-util.h" + #include "string-table.h" +@@ -46,6 +47,7 @@ typedef enum EventSourceType { + _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 + } EventSourceType; + ++ + static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { + [SOURCE_IO] = "io", + [SOURCE_TIME_REALTIME] = "realtime", +@@ -76,7 +78,25 @@ typedef enum WakeupType { + _WAKEUP_TYPE_INVALID = -1, + } WakeupType; + +-#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) ++#define EVENT_SOURCE_IS_TIME(t) \ ++ IN_SET((t), \ ++ SOURCE_TIME_REALTIME, \ ++ SOURCE_TIME_BOOTTIME, \ ++ SOURCE_TIME_MONOTONIC, \ ++ SOURCE_TIME_REALTIME_ALARM, \ ++ SOURCE_TIME_BOOTTIME_ALARM) ++ ++#define EVENT_SOURCE_CAN_RATE_LIMIT(t) \ ++ IN_SET((t), \ ++ SOURCE_IO, \ ++ SOURCE_TIME_REALTIME, \ ++ SOURCE_TIME_BOOTTIME, \ ++ SOURCE_TIME_MONOTONIC, \ ++ SOURCE_TIME_REALTIME_ALARM, \ ++ SOURCE_TIME_BOOTTIME_ALARM, \ ++ SOURCE_SIGNAL, \ ++ SOURCE_DEFER, \ ++ SOURCE_INOTIFY) + + struct inode_data; + +@@ -96,6 +116,7 @@ struct sd_event_source { + bool pending:1; + bool dispatching:1; + bool floating:1; ++ bool ratelimited:1; + + int64_t priority; + unsigned pending_index; +@@ -107,6 +128,10 @@ struct sd_event_source { + + LIST_FIELDS(sd_event_source, sources); + ++ RateLimit rate_limit; ++ ++ /* These are primarily fields relevant for time event sources, but since any event source can ++ * effectively become one when rate-limited, this is part of the common fields. */ + unsigned earliest_index; + unsigned latest_index; + +@@ -266,7 +291,7 @@ struct sd_event { + Hashmap *signal_data; /* indexed by priority */ + + Hashmap *child_sources; +- unsigned n_enabled_child_sources; ++ unsigned n_online_child_sources; + + Set *post_sources; + +@@ -311,12 +336,23 @@ static thread_local sd_event *default_event = NULL; + static void source_disconnect(sd_event_source *s); + static void event_gc_inode_data(sd_event *e, struct inode_data *d); + ++static bool event_source_is_online(sd_event_source *s) { ++ assert(s); ++ return s->enabled != SD_EVENT_OFF && !s->ratelimited; ++} ++ ++static bool event_source_is_offline(sd_event_source *s) { ++ assert(s); ++ return s->enabled == SD_EVENT_OFF || s->ratelimited; ++} ++ + static sd_event *event_resolve(sd_event *e) { + return e == SD_EVENT_DEFAULT ? default_event : e; + } + + static int pending_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; ++ int r; + + assert(x->pending); + assert(y->pending); +@@ -327,23 +363,23 @@ static int pending_prioq_compare(const void *a, const void *b) { + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + ++ /* Non rate-limited ones first. */ ++ r = CMP(!!x->ratelimited, !!y->ratelimited); ++ if (r != 0) ++ return r; ++ + /* Lower priority values first */ +- if (x->priority < y->priority) +- return -1; +- if (x->priority > y->priority) +- return 1; ++ r = CMP(x->priority, y->priority); ++ if (r != 0) ++ return r; + + /* Older entries first */ +- if (x->pending_iteration < y->pending_iteration) +- return -1; +- if (x->pending_iteration > y->pending_iteration) +- return 1; +- +- return 0; ++ return CMP(x->pending_iteration, y->pending_iteration); + } + + static int prepare_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; ++ int r; + + assert(x->prepare); + assert(y->prepare); +@@ -354,29 +390,46 @@ static int prepare_prioq_compare(const void *a, const void *b) { + if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) + return 1; + ++ /* Non rate-limited ones first. */ ++ r = CMP(!!x->ratelimited, !!y->ratelimited); ++ if (r != 0) ++ return r; ++ + /* Move most recently prepared ones last, so that we can stop + * preparing as soon as we hit one that has already been + * prepared in the current iteration */ +- if (x->prepare_iteration < y->prepare_iteration) +- return -1; +- if (x->prepare_iteration > y->prepare_iteration) +- return 1; ++ r = CMP(x->prepare_iteration, y->prepare_iteration); ++ if (r != 0) ++ return r; + + /* Lower priority values first */ +- if (x->priority < y->priority) +- return -1; +- if (x->priority > y->priority) +- return 1; ++ return CMP(x->priority, y->priority); ++} + +- return 0; ++static usec_t time_event_source_next(const sd_event_source *s) { ++ assert(s); ++ ++ /* We have two kinds of event sources that have elapsation times associated with them: the actual ++ * time based ones and the ones for which a ratelimit can be in effect (where we want to be notified ++ * once the ratelimit time window ends). Let's return the next elapsing time depending on what we are ++ * looking at here. */ ++ ++ if (s->ratelimited) { /* If rate-limited the next elapsation is when the ratelimit time window ends */ ++ assert(s->rate_limit.begin != 0); ++ assert(s->rate_limit.interval != 0); ++ return usec_add(s->rate_limit.begin, s->rate_limit.interval); ++ } ++ ++ /* Otherwise this must be a time event source, if not ratelimited */ ++ if (EVENT_SOURCE_IS_TIME(s->type)) ++ return s->time.next; ++ ++ return USEC_INFINITY; + } + + static int earliest_time_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + +- assert(EVENT_SOURCE_IS_TIME(x->type)); +- assert(x->type == y->type); +- + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; +@@ -390,24 +443,30 @@ static int earliest_time_prioq_compare(const void *a, const void *b) { + return 1; + + /* Order by time */ +- if (x->time.next < y->time.next) +- return -1; +- if (x->time.next > y->time.next) +- return 1; +- +- return 0; ++ return CMP(time_event_source_next(x), time_event_source_next(y)); + } + + static usec_t time_event_source_latest(const sd_event_source *s) { +- return usec_add(s->time.next, s->time.accuracy); ++ assert(s); ++ ++ if (s->ratelimited) { /* For ratelimited stuff the earliest and the latest time shall actually be the ++ * same, as we should avoid adding additional inaccuracy on an inaccuracy time ++ * window */ ++ assert(s->rate_limit.begin != 0); ++ assert(s->rate_limit.interval != 0); ++ return usec_add(s->rate_limit.begin, s->rate_limit.interval); ++ } ++ ++ /* Must be a time event source, if not ratelimited */ ++ if (EVENT_SOURCE_IS_TIME(s->type)) ++ return usec_add(s->time.next, s->time.accuracy); ++ ++ return USEC_INFINITY; + } + + static int latest_time_prioq_compare(const void *a, const void *b) { + const sd_event_source *x = a, *y = b; + +- assert(EVENT_SOURCE_IS_TIME(x->type)); +- assert(x->type == y->type); +- + /* Enabled ones first */ + if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) + return -1; +@@ -852,12 +911,12 @@ static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) + * the signalfd for it. */ + + if (sig == SIGCHLD && +- e->n_enabled_child_sources > 0) ++ e->n_online_child_sources > 0) + return; + + if (e->signal_sources && + e->signal_sources[sig] && +- e->signal_sources[sig]->enabled != SD_EVENT_OFF) ++ event_source_is_online(e->signal_sources[sig])) + return; + + /* +@@ -904,11 +963,17 @@ static void event_source_time_prioq_reshuffle(sd_event_source *s) { + struct clock_data *d; + + assert(s); +- assert(EVENT_SOURCE_IS_TIME(s->type)); + + /* Called whenever the event source's timer ordering properties changed, i.e. time, accuracy, + * pending, enable state. Makes sure the two prioq's are ordered properly again. */ +- assert_se(d = event_get_clock_data(s->event, s->type)); ++ ++ if (s->ratelimited) ++ d = &s->event->monotonic; ++ else { ++ assert(EVENT_SOURCE_IS_TIME(s->type)); ++ assert_se(d = event_get_clock_data(s->event, s->type)); ++ } ++ + prioq_reshuffle(d->earliest, s, &s->earliest_index); + prioq_reshuffle(d->latest, s, &s->latest_index); + d->needs_rearm = true; +@@ -949,12 +1014,18 @@ static void source_disconnect(sd_event_source *s) { + case SOURCE_TIME_BOOTTIME: + case SOURCE_TIME_MONOTONIC: + case SOURCE_TIME_REALTIME_ALARM: +- case SOURCE_TIME_BOOTTIME_ALARM: { +- struct clock_data *d; +- assert_se(d = event_get_clock_data(s->event, s->type)); +- event_source_time_prioq_remove(s, d); ++ case SOURCE_TIME_BOOTTIME_ALARM: ++ /* Only remove this event source from the time event source here if it is not ratelimited. If ++ * it is ratelimited, we'll remove it below, separately. Why? Because the clock used might ++ * differ: ratelimiting always uses CLOCK_MONOTONIC, but timer events might use any clock */ ++ ++ if (!s->ratelimited) { ++ struct clock_data *d; ++ assert_se(d = event_get_clock_data(s->event, s->type)); ++ event_source_time_prioq_remove(s, d); ++ } ++ + break; +- } + + case SOURCE_SIGNAL: + if (s->signal.sig > 0) { +@@ -969,9 +1040,9 @@ static void source_disconnect(sd_event_source *s) { + + case SOURCE_CHILD: + if (s->child.pid > 0) { +- if (s->enabled != SD_EVENT_OFF) { +- assert(s->event->n_enabled_child_sources > 0); +- s->event->n_enabled_child_sources--; ++ if (event_source_is_online(s)) { ++ assert(s->event->n_online_child_sources > 0); ++ s->event->n_online_child_sources--; + } + + (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); +@@ -1037,6 +1108,9 @@ static void source_disconnect(sd_event_source *s) { + if (s->prepare) + prioq_remove(s->event->prepare, s, &s->prepare_index); + ++ if (s->ratelimited) ++ event_source_time_prioq_remove(s, &s->event->monotonic); ++ + event = s->event; + + s->type = _SOURCE_EVENT_SOURCE_TYPE_INVALID; +@@ -1458,11 +1532,11 @@ _public_ int sd_event_add_child( + return r; + } + +- e->n_enabled_child_sources++; ++ e->n_online_child_sources++; + + r = event_make_signal_data(e, SIGCHLD, NULL); + if (r < 0) { +- e->n_enabled_child_sources--; ++ e->n_online_child_sources--; + source_free(s); + return r; + } +@@ -2079,7 +2153,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { + if (s->io.fd == fd) + return 0; + +- if (s->enabled == SD_EVENT_OFF) { ++ if (event_source_is_offline(s)) { + s->io.fd = fd; + s->io.registered = false; + } else { +@@ -2146,7 +2220,7 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) + if (r < 0) + return r; + +- if (s->enabled != SD_EVENT_OFF) { ++ if (event_source_is_online(s)) { + r = source_io_register(s, s->enabled, events); + if (r < 0) + return r; +@@ -2249,7 +2323,7 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) + + event_gc_inode_data(s->event, old_inode_data); + +- } else if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { ++ } else if (s->type == SOURCE_SIGNAL && event_source_is_online(s)) { + struct signal_data *old, *d; + + /* Move us from the signalfd belonging to the old +@@ -2296,20 +2370,29 @@ _public_ int sd_event_source_get_enabled(sd_event_source *s, int *ret) { + return s->enabled != SD_EVENT_OFF; + } + +-static int event_source_disable(sd_event_source *s) { ++static int event_source_offline( ++ sd_event_source *s, ++ int enabled, ++ bool ratelimited) { ++ ++ bool was_offline; + int r; + + assert(s); +- assert(s->enabled != SD_EVENT_OFF); ++ assert(enabled == SD_EVENT_OFF || ratelimited); + + /* Unset the pending flag when this event source is disabled */ +- if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { ++ if (s->enabled != SD_EVENT_OFF && ++ enabled == SD_EVENT_OFF && ++ !IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { + r = source_set_pending(s, false); + if (r < 0) + return r; + } + +- s->enabled = SD_EVENT_OFF; ++ was_offline = event_source_is_offline(s); ++ s->enabled = enabled; ++ s->ratelimited = ratelimited; + + switch (s->type) { + +@@ -2330,8 +2413,10 @@ static int event_source_disable(sd_event_source *s) { + break; + + case SOURCE_CHILD: +- assert(s->event->n_enabled_child_sources > 0); +- s->event->n_enabled_child_sources--; ++ if (!was_offline) { ++ assert(s->event->n_online_child_sources > 0); ++ s->event->n_online_child_sources--; ++ } + + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + break; +@@ -2349,26 +2434,42 @@ static int event_source_disable(sd_event_source *s) { + assert_not_reached("Wut? I shouldn't exist."); + } + +- return 0; ++ return 1; + } + +-static int event_source_enable(sd_event_source *s, int enable) { ++static int event_source_online( ++ sd_event_source *s, ++ int enabled, ++ bool ratelimited) { ++ ++ bool was_online; + int r; + + assert(s); +- assert(IN_SET(enable, SD_EVENT_ON, SD_EVENT_ONESHOT)); +- assert(s->enabled == SD_EVENT_OFF); ++ assert(enabled != SD_EVENT_OFF || !ratelimited); + + /* Unset the pending flag when this event source is enabled */ +- if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { ++ if (s->enabled == SD_EVENT_OFF && ++ enabled != SD_EVENT_OFF && ++ !IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { + r = source_set_pending(s, false); + if (r < 0) + return r; + } + ++ /* Are we really ready for onlining? */ ++ if (enabled == SD_EVENT_OFF || ratelimited) { ++ /* Nope, we are not ready for onlining, then just update the precise state and exit */ ++ s->enabled = enabled; ++ s->ratelimited = ratelimited; ++ return 0; ++ } ++ ++ was_online = event_source_is_online(s); ++ + switch (s->type) { + case SOURCE_IO: +- r = source_io_register(s, enable, s->io.events); ++ r = source_io_register(s, enabled, s->io.events); + if (r < 0) + return r; + break; +@@ -2386,13 +2487,13 @@ static int event_source_enable(sd_event_source *s, int enable) { + r = event_make_signal_data(s->event, SIGCHLD, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; +- s->event->n_enabled_child_sources--; ++ s->event->n_online_child_sources--; + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + return r; + } + +- s->event->n_enabled_child_sources++; +- ++ if (!was_online) ++ s->event->n_online_child_sources++; + break; + + case SOURCE_TIME_REALTIME: +@@ -2410,7 +2511,8 @@ static int event_source_enable(sd_event_source *s, int enable) { + assert_not_reached("Wut? I shouldn't exist."); + } + +- s->enabled = enable; ++ s->enabled = enabled; ++ s->ratelimited = ratelimited; + + /* Non-failing operations below */ + switch (s->type) { +@@ -2430,7 +2532,7 @@ static int event_source_enable(sd_event_source *s, int enable) { + break; + } + +- return 0; ++ return 1; + } + + _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { +@@ -2448,7 +2550,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { + return 0; + + if (m == SD_EVENT_OFF) +- r = event_source_disable(s); ++ r = event_source_offline(s, m, s->ratelimited); + else { + if (s->enabled != SD_EVENT_OFF) { + /* Switching from "on" to "oneshot" or back? If that's the case, we can take a shortcut, the +@@ -2457,7 +2559,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { + return 0; + } + +- r = event_source_enable(s, m); ++ r = event_source_online(s, m, s->ratelimited); + } + if (r < 0) + return r; +@@ -2605,6 +2707,96 @@ _public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata) + return ret; + } + ++static int event_source_enter_ratelimited(sd_event_source *s) { ++ int r; ++ ++ assert(s); ++ ++ /* When an event source becomes ratelimited, we place it in the CLOCK_MONOTONIC priority queue, with ++ * the end of the rate limit time window, much as if it was a timer event source. */ ++ ++ if (s->ratelimited) ++ return 0; /* Already ratelimited, this is a NOP hence */ ++ ++ /* Make sure we can install a CLOCK_MONOTONIC event further down. */ ++ r = setup_clock_data(s->event, &s->event->monotonic, CLOCK_MONOTONIC); ++ if (r < 0) ++ return r; ++ ++ /* Timer event sources are already using the earliest/latest queues for the timer scheduling. Let's ++ * first remove them from the prioq appropriate for their own clock, so that we can use the prioq ++ * fields of the event source then for adding it to the CLOCK_MONOTONIC prioq instead. */ ++ if (EVENT_SOURCE_IS_TIME(s->type)) ++ event_source_time_prioq_remove(s, event_get_clock_data(s->event, s->type)); ++ ++ /* Now, let's add the event source to the monotonic clock instead */ ++ r = event_source_time_prioq_put(s, &s->event->monotonic); ++ if (r < 0) ++ goto fail; ++ ++ /* And let's take the event source officially offline */ ++ r = event_source_offline(s, s->enabled, /* ratelimited= */ true); ++ if (r < 0) { ++ event_source_time_prioq_remove(s, &s->event->monotonic); ++ goto fail; ++ } ++ ++ event_source_pp_prioq_reshuffle(s); ++ ++ log_debug("Event source %p (%s) entered rate limit state.", s, strna(s->description)); ++ return 0; ++ ++fail: ++ /* Reinstall time event sources in the priority queue as before. This shouldn't fail, since the queue ++ * space for it should already be allocated. */ ++ if (EVENT_SOURCE_IS_TIME(s->type)) ++ assert_se(event_source_time_prioq_put(s, event_get_clock_data(s->event, s->type)) >= 0); ++ ++ return r; ++} ++ ++static int event_source_leave_ratelimit(sd_event_source *s) { ++ int r; ++ ++ assert(s); ++ ++ if (!s->ratelimited) ++ return 0; ++ ++ /* Let's take the event source out of the monotonic prioq first. */ ++ event_source_time_prioq_remove(s, &s->event->monotonic); ++ ++ /* Let's then add the event source to its native clock prioq again — if this is a timer event source */ ++ if (EVENT_SOURCE_IS_TIME(s->type)) { ++ r = event_source_time_prioq_put(s, event_get_clock_data(s->event, s->type)); ++ if (r < 0) ++ goto fail; ++ } ++ ++ /* Let's try to take it online again. */ ++ r = event_source_online(s, s->enabled, /* ratelimited= */ false); ++ if (r < 0) { ++ /* Do something roughly sensible when this failed: undo the two prioq ops above */ ++ if (EVENT_SOURCE_IS_TIME(s->type)) ++ event_source_time_prioq_remove(s, event_get_clock_data(s->event, s->type)); ++ ++ goto fail; ++ } ++ ++ event_source_pp_prioq_reshuffle(s); ++ ratelimit_reset(&s->rate_limit); ++ ++ log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description)); ++ return 0; ++ ++fail: ++ /* Do something somewhat reasonable when we cannot move an event sources out of ratelimited mode: ++ * simply put it back in it, maybe we can then process it more successfully next iteration. */ ++ assert_se(event_source_time_prioq_put(s, &s->event->monotonic) >= 0); ++ ++ return r; ++} ++ + static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) { + usec_t c; + assert(e); +@@ -2703,7 +2895,7 @@ static int event_arm_timer( + d->needs_rearm = false; + + a = prioq_peek(d->earliest); +- if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) { ++ if (!a || a->enabled == SD_EVENT_OFF || time_event_source_next(a) == USEC_INFINITY) { + + if (d->fd < 0) + return 0; +@@ -2723,7 +2915,7 @@ static int event_arm_timer( + b = prioq_peek(d->latest); + assert_se(b && b->enabled != SD_EVENT_OFF); + +- t = sleep_between(e, a->time.next, time_event_source_latest(b)); ++ t = sleep_between(e, time_event_source_next(a), time_event_source_latest(b)); + if (d->next == t) + return 0; + +@@ -2802,10 +2994,22 @@ static int process_timer( + + for (;;) { + s = prioq_peek(d->earliest); +- if (!s || +- s->time.next > n || +- s->enabled == SD_EVENT_OFF || +- s->pending) ++ if (!s || time_event_source_next(s) > n) ++ break; ++ ++ if (s->ratelimited) { ++ /* This is an event sources whose ratelimit window has ended. Let's turn it on ++ * again. */ ++ assert(s->ratelimited); ++ ++ r = event_source_leave_ratelimit(s); ++ if (r < 0) ++ return r; ++ ++ continue; ++ } ++ ++ if (s->enabled == SD_EVENT_OFF || s->pending) + break; + + r = source_set_pending(s, true); +@@ -2851,7 +3055,7 @@ static int process_child(sd_event *e) { + if (s->pending) + continue; + +- if (s->enabled == SD_EVENT_OFF) ++ if (event_source_is_offline(s)) + continue; + + zero(s->child.siginfo); +@@ -3024,7 +3228,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { + + LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { + +- if (s->enabled == SD_EVENT_OFF) ++ if (event_source_is_offline(s)) + continue; + + r = source_set_pending(s, true); +@@ -3060,7 +3264,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { + * sources if IN_IGNORED or IN_UNMOUNT is set. */ + LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { + +- if (s->enabled == SD_EVENT_OFF) ++ if (event_source_is_offline(s)) + continue; + + if ((d->buffer.ev.mask & (IN_IGNORED|IN_UNMOUNT)) == 0 && +@@ -3099,6 +3303,7 @@ static int process_inotify(sd_event *e) { + } + + static int source_dispatch(sd_event_source *s) { ++ _cleanup_(sd_event_unrefp) sd_event *saved_event = NULL; + EventSourceType saved_type; + int r = 0; + +@@ -3109,6 +3314,20 @@ static int source_dispatch(sd_event_source *s) { + * the event. */ + saved_type = s->type; + ++ /* Similar, store a reference to the event loop object, so that we can still access it after the ++ * callback might have invalidated/disconnected the event source. */ ++ saved_event = sd_event_ref(s->event); ++ ++ /* Check if we hit the ratelimit for this event source, if so, let's disable it. */ ++ assert(!s->ratelimited); ++ if (!ratelimit_below(&s->rate_limit)) { ++ r = event_source_enter_ratelimited(s); ++ if (r < 0) ++ return r; ++ ++ return 1; ++ } ++ + if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { + r = source_set_pending(s, false); + if (r < 0) +@@ -3235,7 +3454,7 @@ static int event_prepare(sd_event *e) { + sd_event_source *s; + + s = prioq_peek(e->prepare); +- if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF) ++ if (!s || s->prepare_iteration == e->iteration || event_source_is_offline(s)) + break; + + s->prepare_iteration = e->iteration; +@@ -3269,7 +3488,7 @@ static int dispatch_exit(sd_event *e) { + assert(e); + + p = prioq_peek(e->exit); +- if (!p || p->enabled == SD_EVENT_OFF) { ++ if (!p || event_source_is_offline(p)) { + e->state = SD_EVENT_FINISHED; + return 0; + } +@@ -3291,7 +3510,7 @@ static sd_event_source* event_next_pending(sd_event *e) { + if (!p) + return NULL; + +- if (p->enabled == SD_EVENT_OFF) ++ if (event_source_is_offline(p)) + return NULL; + + return p; +@@ -3844,3 +4063,53 @@ _public_ int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_d + + return !!s->destroy_callback; + } ++ ++_public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval, unsigned burst) { ++ int r; ++ ++ assert_return(s, -EINVAL); ++ ++ /* Turning on ratelimiting on event source types that don't support it, is a loggable offense. Doing ++ * so is a programming error. */ ++ assert_return(EVENT_SOURCE_CAN_RATE_LIMIT(s->type), -EDOM); ++ ++ /* When ratelimiting is configured we'll always reset the rate limit state first and start fresh, ++ * non-ratelimited. */ ++ r = event_source_leave_ratelimit(s); ++ if (r < 0) ++ return r; ++ ++ RATELIMIT_INIT(s->rate_limit, interval, burst); ++ return 0; ++} ++ ++_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) { ++ assert_return(s, -EINVAL); ++ ++ /* Querying whether an event source has ratelimiting configured is not a loggable offsense, hence ++ * don't use assert_return(). Unlike turning on ratelimiting it's not really a programming error */ ++ if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type)) ++ return -EDOM; ++ ++ if (!ratelimit_configured(&s->rate_limit)) ++ return -ENOEXEC; ++ ++ if (ret_interval) ++ *ret_interval = s->rate_limit.interval; ++ if (ret_burst) ++ *ret_burst = s->rate_limit.burst; ++ ++ return 0; ++} ++ ++_public_ int sd_event_source_is_ratelimited(sd_event_source *s) { ++ assert_return(s, -EINVAL); ++ ++ if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type)) ++ return false; ++ ++ if (!ratelimit_configured(&s->rate_limit)) ++ return false; ++ ++ return s->ratelimited; ++} +diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h +index 9876be01c6..a17a9b3488 100644 +--- a/src/systemd/sd-event.h ++++ b/src/systemd/sd-event.h +@@ -144,6 +144,9 @@ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid); + int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret); + int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback); + int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret); ++int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst); ++int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst); ++int sd_event_source_is_ratelimited(sd_event_source *s); + + /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); diff --git a/SOURCES/0560-test-add-ratelimiting-test.patch b/SOURCES/0560-test-add-ratelimiting-test.patch new file mode 100644 index 0000000..8e398e1 --- /dev/null +++ b/SOURCES/0560-test-add-ratelimiting-test.patch @@ -0,0 +1,127 @@ +From c35ba62cd6f337c4eef64cdc3b9796f988802229 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 23 Nov 2020 18:04:57 +0100 +Subject: [PATCH] test: add ratelimiting test + +(Taken from Michal's #17274 by Lennart, and slightly adjusted) + +(cherry picked from commit 68d890651781904a4c762ac866af36e30c4f7ff8) + +Related: #1819868 +--- + src/libsystemd/sd-event/test-event.c | 96 ++++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c +index bde00cf719..e3ee4cd5c3 100644 +--- a/src/libsystemd/sd-event/test-event.c ++++ b/src/libsystemd/sd-event/test-event.c +@@ -482,6 +482,100 @@ static void test_inotify(unsigned n_create_events) { + sd_event_unref(e); + } + ++ ++static int ratelimit_io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { ++ unsigned *c = (unsigned*) userdata; ++ *c += 1; ++ return 0; ++} ++ ++static int ratelimit_time_handler(sd_event_source *s, uint64_t usec, void *userdata) { ++ int r; ++ ++ r = sd_event_source_set_enabled(s, SD_EVENT_ON); ++ if (r < 0) ++ log_warning_errno(r, "Failed to turn on notify event source: %m"); ++ ++ r = sd_event_source_set_time(s, usec + 1000); ++ if (r < 0) ++ log_error_errno(r, "Failed to restart watchdog event source: %m"); ++ ++ unsigned *c = (unsigned*) userdata; ++ *c += 1; ++ ++ return 0; ++} ++ ++static void test_ratelimit(void) { ++ _cleanup_close_pair_ int p[2] = {-1, -1}; ++ _cleanup_(sd_event_unrefp) sd_event *e = NULL; ++ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; ++ uint64_t interval; ++ unsigned count, burst; ++ ++ assert_se(sd_event_default(&e) >= 0); ++ assert_se(pipe2(p, O_CLOEXEC|O_NONBLOCK) >= 0); ++ ++ assert_se(sd_event_add_io(e, &s, p[0], EPOLLIN, ratelimit_io_handler, &count) >= 0); ++ assert_se(sd_event_source_set_description(s, "test-ratelimit-io") >= 0); ++ assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 5) >= 0); ++ assert_se(sd_event_source_get_ratelimit(s, &interval, &burst) >= 0); ++ assert_se(interval == 1 * USEC_PER_SEC && burst == 5); ++ ++ assert_se(write(p[1], "1", 1) == 1); ++ ++ count = 0; ++ for (unsigned i = 0; i < 10; i++) { ++ log_debug("slow loop iteration %u", i); ++ assert_se(sd_event_run(e, UINT64_MAX) >= 0); ++ assert_se(usleep(250 * USEC_PER_MSEC) >= 0); ++ } ++ ++ assert_se(sd_event_source_is_ratelimited(s) == 0); ++ assert_se(count == 10); ++ log_info("ratelimit_io_handler: called %d times, event source not ratelimited", count); ++ ++ assert_se(sd_event_source_set_ratelimit(s, 0, 0) >= 0); ++ assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 5) >= 0); ++ ++ count = 0; ++ for (unsigned i = 0; i < 10; i++) { ++ log_debug("fast event loop iteration %u", i); ++ assert_se(sd_event_run(e, UINT64_MAX) >= 0); ++ assert_se(usleep(10) >= 0); ++ } ++ log_info("ratelimit_io_handler: called %d times, event source got ratelimited", count); ++ assert_se(count < 10); ++ ++ s = sd_event_source_unref(s); ++ safe_close_pair(p); ++ ++ count = 0; ++ ++ assert_se(sd_event_add_time(e, &s, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 1000, 0, ratelimit_time_handler, &count) >= 0); ++ assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) == 0); ++ ++ do { ++ assert_se(sd_event_run(e, UINT64_MAX) >= 0); ++ } while (!sd_event_source_is_ratelimited(s)); ++ ++ log_info("ratelimit_time_handler: called %d times, event source got ratelimited", count); ++ assert_se(count == 10); ++ ++ /* In order to get rid of active rate limit client needs to disable it explicitely */ ++ assert_se(sd_event_source_set_ratelimit(s, 0, 0) >= 0); ++ assert_se(!sd_event_source_is_ratelimited(s)); ++ ++ assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) >= 0); ++ ++ do { ++ assert_se(sd_event_run(e, UINT64_MAX) >= 0); ++ } while (!sd_event_source_is_ratelimited(s)); ++ ++ log_info("ratelimit_time_handler: called 10 more times, event source got ratelimited"); ++ assert_se(count == 20); ++} ++ + int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); +@@ -494,5 +588,7 @@ int main(int argc, char *argv[]) { + test_inotify(100); /* should work without overflow */ + test_inotify(33000); /* should trigger a q overflow */ + ++ test_ratelimit(); ++ + return 0; + } diff --git a/SOURCES/0561-core-prevent-excessive-proc-self-mountinfo-parsing.patch b/SOURCES/0561-core-prevent-excessive-proc-self-mountinfo-parsing.patch new file mode 100644 index 0000000..71075e0 --- /dev/null +++ b/SOURCES/0561-core-prevent-excessive-proc-self-mountinfo-parsing.patch @@ -0,0 +1,29 @@ +From 51737206afaa10d902c86ec9b5ec97cf425039c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 9 Jul 2020 18:16:44 +0200 +Subject: [PATCH] core: prevent excessive /proc/self/mountinfo parsing + +(cherry picked from commit d586f642fd90e3bb378f7b6d3e3a64a753e51756) + +Resolves: #1819868 +--- + src/core/mount.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 2746372db2..076dfd06a3 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1763,6 +1763,12 @@ static void mount_enumerate(Manager *m) { + goto fail; + } + ++ r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, 5); ++ if (r < 0) { ++ log_error_errno(r, "Failed to enable rate limit for mount events: %m"); ++ goto fail; ++ } ++ + (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); + } + diff --git a/SOURCES/0562-udev-run-link_update-with-increased-retry-count-in-s.patch b/SOURCES/0562-udev-run-link_update-with-increased-retry-count-in-s.patch new file mode 100644 index 0000000..90fb6ad --- /dev/null +++ b/SOURCES/0562-udev-run-link_update-with-increased-retry-count-in-s.patch @@ -0,0 +1,44 @@ +From 1f3165bda13c8572c8c31d23c998835c4e2ad8f3 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Thu, 4 Mar 2021 17:35:22 +0100 +Subject: [PATCH] udev: run link_update() with increased retry count in second + invocation + +In PR #17431 we have introduced retry loop in link_update() in order to +maximize the chance that we end up with correct target when there are +multiple contenders for given symlink. + +Number of iterations in retry loop is either 1 or +LINK_UPDATE_MAX_RETRIES, depending on the value of 'initialized' db +flag. When device appears for the first time we need to set the +flag before calling link_update() via update_devnode() for the second +time to make sure we run the second invocation with higher retry loop +counter. + +(cherry picked from commit 996c83903da5bf8b371314b4207ff97afeef65a4) + +Related: #1931947 +--- + src/udev/udev-event.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c +index 9004634f65..eaec05523b 100644 +--- a/src/udev/udev-event.c ++++ b/src/udev/udev-event.c +@@ -934,14 +934,13 @@ void udev_event_execute_rules(struct udev_event *event, + /* (re)write database file */ + udev_device_tag_index(dev, event->dev_db, true); + udev_device_update_db(dev); ++ udev_device_set_is_initialized(dev); + + /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database, + * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure + * symlinks point to devices that claim them with the highest priority. */ + update_devnode(event); + +- udev_device_set_is_initialized(dev); +- + event->dev_db = udev_device_unref(event->dev_db); + } + } diff --git a/SOURCES/0563-pam-systemd-use-secure_getenv-rather-than-getenv.patch b/SOURCES/0563-pam-systemd-use-secure_getenv-rather-than-getenv.patch new file mode 100644 index 0000000..2eb7fc5 --- /dev/null +++ b/SOURCES/0563-pam-systemd-use-secure_getenv-rather-than-getenv.patch @@ -0,0 +1,89 @@ +From fcd9a141d08d521c01dc1a1c06a8d43a2337a392 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 4 Feb 2019 10:23:43 +0100 +Subject: [PATCH] pam-systemd: use secure_getenv() rather than getenv() + +And explain why in a comment. + +(cherry picked from commit 83d4ab55336ff8a0643c6aa627b31e351a24040a) + +CVE-2019-3842 + +Resolves: #1687514 +--- + src/login/pam_systemd.c | 55 ++++++++++++++++++++++++----------------- + 1 file changed, 32 insertions(+), 23 deletions(-) + +diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c +index 1fbf6ba585..78ddb7d398 100644 +--- a/src/login/pam_systemd.c ++++ b/src/login/pam_systemd.c +@@ -274,6 +274,33 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con + return 0; + } + ++static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) { ++ const char *v; ++ ++ assert(handle); ++ assert(key); ++ ++ /* Looks for an environment variable, preferrably in the environment block associated with the ++ * specified PAM handle, falling back to the process' block instead. 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; ++ ++ /* 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; ++ ++ return fallback; ++} ++ + _public_ PAM_EXTERN int pam_sm_open_session( + pam_handle_t *handle, + int flags, +@@ -352,29 +379,11 @@ _public_ PAM_EXTERN int pam_sm_open_session( + pam_get_item(handle, PAM_RUSER, (const void**) &remote_user); + pam_get_item(handle, PAM_RHOST, (const void**) &remote_host); + +- seat = pam_getenv(handle, "XDG_SEAT"); +- if (isempty(seat)) +- seat = getenv("XDG_SEAT"); +- +- cvtnr = pam_getenv(handle, "XDG_VTNR"); +- if (isempty(cvtnr)) +- cvtnr = getenv("XDG_VTNR"); +- +- type = pam_getenv(handle, "XDG_SESSION_TYPE"); +- if (isempty(type)) +- type = getenv("XDG_SESSION_TYPE"); +- if (isempty(type)) +- type = type_pam; +- +- class = pam_getenv(handle, "XDG_SESSION_CLASS"); +- if (isempty(class)) +- class = getenv("XDG_SESSION_CLASS"); +- if (isempty(class)) +- class = class_pam; +- +- desktop = pam_getenv(handle, "XDG_SESSION_DESKTOP"); +- if (isempty(desktop)) +- desktop = getenv("XDG_SESSION_DESKTOP"); ++ seat = getenv_harder(handle, "XDG_SEAT", NULL); ++ cvtnr = getenv_harder(handle, "XDG_VTNR", NULL); ++ type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam); ++ class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam); ++ desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", NULL); + + tty = strempty(tty); + diff --git a/SOURCES/20-grubby.install b/SOURCES/20-grubby.install new file mode 100755 index 0000000..a67856c --- /dev/null +++ b/SOURCES/20-grubby.install @@ -0,0 +1,58 @@ +#!/bin/bash + +if [[ ! -x /sbin/new-kernel-pkg ]]; then + exit 0 +fi + +COMMAND="$1" +KERNEL_VERSION="$2" +BOOT_DIR_ABS="$3" +KERNEL_IMAGE="$4" + +KERNEL_DIR="${KERNEL_IMAGE%/*}" +[[ "$KERNEL_VERSION" == *\+* ]] && flavor=-"${KERNEL_VERSION##*+}" +case "$COMMAND" in + add) + if [[ "${KERNEL_DIR}" != "/boot" ]]; then + for i in \ + "$KERNEL_IMAGE" \ + "$KERNEL_DIR"/System.map \ + "$KERNEL_DIR"/config \ + "$KERNEL_DIR"/zImage.stub \ + "$KERNEL_DIR"/dtb \ + ; do + [[ -e "$i" ]] || continue + cp -aT "$i" "/boot/${i##*/}-${KERNEL_VERSION}" + command -v restorecon &>/dev/null && \ + restorecon -R "/boot/${i##*/}-${KERNEL_VERSION}" + done + # hmac is .vmlinuz-.hmac so needs a special treatment + i="$KERNEL_DIR/.${KERNEL_IMAGE##*/}.hmac" + if [[ -e "$i" ]]; then + cp -a "$i" "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + command -v restorecon &>/dev/null && \ + restorecon "/boot/.${KERNEL_IMAGE##*/}-${KERNEL_VERSION}.hmac" + fi + # symvers is symvers-.gz so needs a special treatment + i="$KERNEL_DIR/symvers.gz" + if [[ -e "$i" ]]; then + cp -a "$i" "/boot/symvers-${KERNEL_VERSION}.gz" + command -v restorecon &>/dev/null && \ + restorecon "/boot/symvers-${KERNEL_VERSION}.gz" + fi + fi + /sbin/new-kernel-pkg --package "kernel${flavor}" --install "$KERNEL_VERSION" || exit $? + /sbin/new-kernel-pkg --package "kernel${flavor}" --mkinitrd --dracut --depmod --update "$KERNEL_VERSION" || exit $? + /sbin/new-kernel-pkg --package "kernel${flavor}" --rpmposttrans "$KERNEL_VERSION" || exit $? + ;; + remove) + /sbin/new-kernel-pkg --package "kernel${flavor+-$flavor}" --rminitrd --rmmoddep --remove "$KERNEL_VERSION" || exit $? + ;; + *) + ;; +esac + +# skip other installation plugins, if we can't find a boot loader spec conforming setup +if ! [[ -d /boot/loader/entries || -L /boot/loader/entries ]]; then + exit 77 +fi diff --git a/SOURCES/20-yama-ptrace.conf b/SOURCES/20-yama-ptrace.conf new file mode 100644 index 0000000..4fbaf97 --- /dev/null +++ b/SOURCES/20-yama-ptrace.conf @@ -0,0 +1,42 @@ +# The ptrace system call is used for interprocess services, +# communication and introspection (like synchronisation, signaling, +# debugging, tracing and profiling) of processes. +# +# Usage of ptrace is restricted by normal user permissions. Normal +# unprivileged processes cannot use ptrace on processes that they +# cannot send signals to or processes that are running set-uid or +# set-gid. Nevertheless, processes running under the same uid will +# usually be able to ptrace one another. +# +# Fedora enables the Yama security mechanism which restricts ptrace +# even further. Sysctl setting kernel.yama.ptrace_scope can have one +# of the following values: +# +# 0 - Normal ptrace security permissions. +# 1 - Restricted ptrace. Only child processes plus normal permissions. +# 2 - Admin-only attach. Only executables with CAP_SYS_PTRACE. +# 3 - No attach. No process may call ptrace at all. Irrevocable. +# +# For more information see Documentation/security/Yama.txt in the +# kernel sources. +# +# The default is 1., which allows tracing of child processes, but +# forbids tracing of arbitrary processes. This allows programs like +# gdb or strace to work when the most common way of having the +# debugger start the debuggee is used: +# gdb /path/to/program ... +# Attaching to already running programs is NOT allowed: +# gdb -p ... +# This default setting is suitable for the common case, because it +# reduces the risk that one hacked process can be used to attack other +# processes. (For example, a hacked firefox process in a user session +# will not be able to ptrace the keyring process and extract passwords +# stored only in memory.) +# +# Developers and administrators might want to disable those protections +# to be able to attach debuggers to existing processes. Use +# sysctl kernel.yama.ptrace_scope=0 +# for change the setting temporarily, or copy this file to +# /etc/sysctl.d/20-yama-ptrace.conf to set it for future boots. + +kernel.yama.ptrace_scope = 0 diff --git a/SOURCES/inittab b/SOURCES/inittab new file mode 100644 index 0000000..3f5e83c --- /dev/null +++ b/SOURCES/inittab @@ -0,0 +1,16 @@ +# inittab is no longer used. +# +# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM. +# +# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target +# +# systemd uses 'targets' instead of runlevels. By default, there are two main targets: +# +# multi-user.target: analogous to runlevel 3 +# graphical.target: analogous to runlevel 5 +# +# To view current default target, run: +# systemctl get-default +# +# To set a default target, run: +# systemctl set-default TARGET.target diff --git a/SOURCES/purge-nobody-user b/SOURCES/purge-nobody-user new file mode 100755 index 0000000..66404fe --- /dev/null +++ b/SOURCES/purge-nobody-user @@ -0,0 +1,101 @@ +#!/bin/bash -eu + +if [ $UID -ne 0 ]; then + echo "WARNING: This script needs to run as root to be effective" + exit 1 +fi + +export SYSTEMD_NSS_BYPASS_SYNTHETIC=1 + +if [ "${1:-}" = "--ignore-journal" ]; then + shift + ignore_journal=1 +else + ignore_journal=0 +fi + +echo "Checking processes..." +if ps h -u 99 | grep .; then + echo "ERROR: ps reports processes with UID 99!" + exit 2 +fi +echo "... not found" + +echo "Checking UTMP..." +if w -h 199 | grep . ; then + echo "ERROR: w reports UID 99 as active!" + exit 2 +fi +if w -h nobody | grep . ; then + echo "ERROR: w reports user nobody as active!" + exit 2 +fi +echo "... not found" + +echo "Checking the journal..." +if [ "$ignore_journal" = 0 ] && journalctl -q -b -n10 _UID=99 | grep . ; then + echo "ERROR: journalctl reports messages from UID 99 in current boot!" + exit 2 +fi +echo "... not found" + +echo "Looking for files in /etc, /run, /tmp, and /var..." +if find /etc /run /tmp /var -uid 99 -print | grep -m 10 . ; then + echo "ERROR: found files belonging to UID 99" + exit 2 +fi +echo "... not found" + +echo "Checking if nobody is defined correctly..." +if getent passwd nobody | + grep '^nobody:[x*]:65534:65534:.*:/:/sbin/nologin'; +then + echo "OK, nothing to do." + exit 0 +else + echo "NOTICE: User nobody is not defined correctly" +fi + +echo "Checking if nfsnobody or something else is using the uid..." +if getent passwd 65534 | grep . ; then + echo "NOTICE: will have to remove this user" +else + echo "... not found" +fi + +if [ "${1:-}" = "-x" ]; then + if getent passwd nobody >/dev/null; then + # this will remove both the user and the group. + ( set -x + userdel nobody + ) + fi + + if getent passwd 65534 >/dev/null; then + # Make sure the uid is unused. This should free gid too. + name="$(getent passwd 65534 | cut -d: -f1)" + ( set -x + userdel "$name" + ) + fi + + if grep -qE '^(passwd|group):.*\bsss\b' /etc/nsswitch.conf; then + echo "Sleeping, so sss can catch up" + sleep 3 + fi + + if getent group 65534; then + # Make sure the gid is unused, even if uid wasn't. + name="$(getent group 65534 | cut -d: -f1)" + ( set -x + groupdel "$name" + ) + fi + + # systemd-sysusers uses the same gid and uid + ( set -x + systemd-sysusers --inline 'u nobody 65534 "Kernel Overflow User" / /sbin/nologin' + ) +else + echo "Pass '-x' to perform changes" +fi diff --git a/SOURCES/rc.local b/SOURCES/rc.local new file mode 100644 index 0000000..4666070 --- /dev/null +++ b/SOURCES/rc.local @@ -0,0 +1,14 @@ +#!/bin/bash +# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES +# +# It is highly advisable to create own systemd services or udev rules +# to run scripts during boot instead of using this file. +# +# In contrast to previous versions due to parallel execution during boot +# this script will NOT be run after all other services. +# +# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure +# that this script will be executed during boot. + +touch /var/lock/subsys/local + diff --git a/SOURCES/split-files.py b/SOURCES/split-files.py new file mode 100644 index 0000000..262ee04 --- /dev/null +++ b/SOURCES/split-files.py @@ -0,0 +1,116 @@ +import re, sys, os, collections + +buildroot = sys.argv[1] +known_files = sys.stdin.read().splitlines() +known_files = {line.split()[-1]:line for line in known_files} + +def files(root): + os.chdir(root) + todo = collections.deque(['.']) + while todo: + n = todo.pop() + files = os.scandir(n) + for file in files: + yield file + if file.is_dir() and not file.is_symlink(): + todo.append(file) + +o_libs = open('.file-list-libs', 'w') +o_udev = open('.file-list-udev', 'w') +o_pam = open('.file-list-pam', 'w') +o_devel = open('.file-list-devel', 'w') +o_container = open('.file-list-container', 'w') +o_remote = open('.file-list-remote', 'w') +o_tests = open('.file-list-tests', 'w') +o_rest = open('.file-list-rest', 'w') +for file in files(buildroot): + n = file.path[1:] + if re.match(r'''/usr/(share|include)$| + /usr/share/man(/man.|)$| + /usr/share/zsh(/site-functions|)$| + /usr/share/dbus-1$| + /usr/share/dbus-1/system.d$| + /usr/share/dbus-1/(system-|)services$| + /usr/share/polkit-1(/actions|/rules.d|)$| + /usr/share/pkgconfig$| + /usr/share/bash-completion(/completions|)$| + /usr(/lib|/lib64|/bin|/sbin|)$| + /usr/lib.*/(security|pkgconfig)$| + /usr/lib/rpm(/macros.d|)$| + /usr/lib/firewalld(/services|)$| + /usr/share/(locale|licenses|doc)| # no $ + /etc(/pam\.d|/xdg|/X11|/X11/xinit|/X11.*\.d|)$| + /etc/(dnf|dnf/protected.d)$| + /usr/(src|lib/debug)| # no $ + /var(/cache|/log|/lib|/run|)$ + ''', n, re.X): + continue + if '/security/pam_' in n: + o = o_pam + elif re.search(r'/lib.*\.pc|/man3/|/usr/include|(? + + systemd-journal-gatewayd + Journal Gateway Service + + diff --git a/SOURCES/systemd-journal-remote.xml b/SOURCES/systemd-journal-remote.xml new file mode 100644 index 0000000..e115a12 --- /dev/null +++ b/SOURCES/systemd-journal-remote.xml @@ -0,0 +1,6 @@ + + + systemd-journal-remote + Journal Remote Sink + + diff --git a/SOURCES/systemd-udev-trigger-no-reload.conf b/SOURCES/systemd-udev-trigger-no-reload.conf new file mode 100644 index 0000000..c879427 --- /dev/null +++ b/SOURCES/systemd-udev-trigger-no-reload.conf @@ -0,0 +1,3 @@ +[Unit] +# https://bugzilla.redhat.com/show_bug.cgi?id=1378974#c17 +RefuseManualStop=true diff --git a/SOURCES/systemd-user b/SOURCES/systemd-user new file mode 100644 index 0000000..2725df9 --- /dev/null +++ b/SOURCES/systemd-user @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# Used by systemd --user instances. + +account include system-auth + +session required pam_selinux.so close +session required pam_selinux.so nottys open +session required pam_loginuid.so +session include system-auth diff --git a/SOURCES/triggers.systemd b/SOURCES/triggers.systemd new file mode 100644 index 0000000..04abfd1 --- /dev/null +++ b/SOURCES/triggers.systemd @@ -0,0 +1,109 @@ +# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# Copyright 2015 Zbigniew Jędrzejewski-Szmek +# Copyright 2018 Neal Gompa +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +# The contents of this are an example to be copied into systemd.spec. +# +# Minimum rpm version supported: 4.13.0 + +%transfiletriggerin -P 900900 -- /usr/lib/systemd/system /etc/systemd/system +# This script will run after any package is initially installed or +# upgraded. We care about the case where a package is initially +# installed, because other cases are covered by the *un scriptlets, +# so sometimes we will reload needlessly. +if test -d /run/systemd/system; then + %{_bindir}/systemctl daemon-reload +fi + +%transfiletriggerun -- /usr/lib/systemd/system /etc/systemd/system +# On removal, we need to run daemon-reload after any units have been +# removed. %transfiletriggerpostun would be ideal, but it does not get +# executed for some reason. +# On upgrade, we need to run daemon-reload after any new unit files +# have been installed, but before %postun scripts in packages get +# executed. %transfiletriggerun gets the right list of files +# but it is invoked too early (before changes happen). +# %filetriggerpostun happens at the right time, but it fires for +# every package. +# To execute the reload at the right time, we create a state +# file in %transfiletriggerun and execute the daemon-reload in +# the first %filetriggerpostun. + +if test -d "/run/systemd/system"; then + mkdir -p "%{_localstatedir}/lib/rpm-state/systemd" + touch "%{_localstatedir}/lib/rpm-state/systemd/needs-reload" +fi + +%filetriggerpostun -P 1000100 -- /usr/lib/systemd/system /etc/systemd/system +if test -f "%{_localstatedir}/lib/rpm-state/systemd/needs-reload"; then + rm -rf "%{_localstatedir}/lib/rpm-state/systemd" + %{_bindir}/systemctl daemon-reload +fi + +%transfiletriggerin -P 100700 -- /usr/lib/sysusers.d +# This script will process files installed in /usr/lib/sysusers.d to create +# specified users automatically. The priority is set such that it +# will run before the tmpfiles file trigger. +if test -d /run/systemd/system; then + %{_bindir}/systemd-sysusers +fi + +%transfiletriggerin -P 100500 -- /usr/lib/tmpfiles.d +# This script will process files installed in /usr/lib/tmpfiles.d to create +# tmpfiles automatically. The priority is set such that it will run +# after the sysusers file trigger, but before any other triggers. +if test -d /run/systemd/system; then + %{_bindir}/systemd-tmpfiles --create +fi + +%transfiletriggerin udev -- /usr/lib/udev/hwdb.d +# This script will automatically invoke hwdb update if files have been +# installed or updated in /usr/lib/udev/hwdb.d. +if test -d /run/systemd/system; then + %{_bindir}/systemd-hwdb update +fi + +%transfiletriggerin -- /usr/lib/systemd/catalog +# This script will automatically invoke journal catalog update if files +# have been installed or updated in /usr/lib/systemd/catalog. +if test -d /run/systemd/system; then + %{_bindir}/journalctl --update-catalog +fi + +%transfiletriggerin udev -- /usr/lib/udev/rules.d +# This script will automatically update udev with new rules if files +# have been installed or updated in /usr/lib/udev/rules.d. +if test -d /run/systemd/system; then + %{_bindir}/udevadm control --reload +fi + +%transfiletriggerin -- /usr/lib/sysctl.d +# This script will automatically apply sysctl rules if files have been +# installed or updated in /usr/lib/sysctl.d. +if test -d /run/systemd/system; then + /usr/lib/systemd/systemd-sysctl +fi + +%transfiletriggerin -- /usr/lib/binfmt.d +# This script will automatically apply binfmt rules if files have been +# installed or updated in /usr/lib/binfmt.d. +if test -d /run/systemd/system; then + /usr/lib/systemd/systemd-binfmt +fi diff --git a/SOURCES/yum-protect-systemd.conf b/SOURCES/yum-protect-systemd.conf new file mode 100644 index 0000000..39426d7 --- /dev/null +++ b/SOURCES/yum-protect-systemd.conf @@ -0,0 +1,2 @@ +systemd +systemd-udev diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec new file mode 100644 index 0000000..23e5b66 --- /dev/null +++ b/SPECS/systemd.spec @@ -0,0 +1,3509 @@ +#global gitcommit 10e465b5321bd53c1fc59ffab27e724535c6bc0f +%{?gitcommit:%global gitcommitshort %(c=%{gitcommit}; echo ${c:0:7})} + +# We ship a .pc file but don't want to have a dep on pkg-config. We +# strip the automatically generated dep here and instead co-own the +# directory. +%global __requires_exclude pkg-config + +%global pkgdir %{_prefix}/lib/systemd +%global system_unit_dir %{pkgdir}/system +%global user_unit_dir %{pkgdir}/user + +Name: systemd +Url: http://www.freedesktop.org/wiki/Software/systemd +Version: 239 +Release: 45%{?dist} +# For a breakdown of the licensing, see README +License: LGPLv2+ and MIT and GPLv2+ +Summary: System and Service Manager + +# download tarballs with "spectool -g systemd.spec" +%if %{defined gitcommit} +Source0: https://github.com/systemd/systemd-stable/archive/%{?gitcommit}.tar.gz#/%{name}-%{gitcommitshort}.tar.gz +%else +Source0: https://github.com/systemd/systemd/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz +%endif +# This file must be available before %%prep. +# It is generated during systemd build and can be found in src/core/. +Source1: triggers.systemd +Source2: split-files.py +Source3: purge-nobody-user + +# Prevent accidental removal of the systemd package +Source4: yum-protect-systemd.conf + +Source5: inittab +Source6: sysctl.conf.README +Source7: systemd-journal-remote.xml +Source8: systemd-journal-gatewayd.xml +Source9: 20-yama-ptrace.conf +Source10: systemd-udev-trigger-no-reload.conf +Source11: 20-grubby.install +Source12: systemd-user +Source13: rc.local + +%if 0 +GIT_DIR=../../src/systemd/.git git format-patch-ab --no-signature -M -N v235..v235-stable +i=1; for j in 00*patch; do printf "Patch%04d: %s\n" $i $j; i=$((i+1));done|xclip +GIT_DIR=../../src/systemd/.git git diffab -M v233..master@{2017-06-15} -- hwdb/[67]* hwdb/parse_hwdb.py > hwdb.patch +%endif + +# RHEL-specific +Patch0001: 0001-build-sys-Detect-whether-struct-statx-is-defined-in-.patch +Patch0002: 0002-logind-set-RemoveIPC-to-false-by-default.patch +Patch0003: 0003-pid1-bump-DefaultTasksMax-to-80-of-the-kernel-pid.ma.patch +Patch0004: 0004-Avoid-tmp-being-mounted-as-tmpfs-without-the-user-s-.patch +Patch0005: 0005-pid1-bump-maximum-number-of-process-in-user-slice-to.patch +Patch0006: 0006-rules-automatically-online-hot-plugged-CPUs.patch +Patch0007: 0007-rules-add-rule-for-naming-Dell-iDRAC-USB-Virtual-NIC.patch +Patch0008: 0008-rules-enable-memory-hotplug.patch +Patch0009: 0009-rules-reload-sysctl-settings-when-the-bridge-module-.patch +Patch0010: 0010-rules-load-sg-module.patch +Patch0011: 0011-rules-prandom-character-device-node-permissions.patch +Patch0012: 0012-rules-load-sg-driver-also-when-scsi_target-appears-4.patch +Patch0013: 0013-rules-don-t-hoplug-memory-on-s390x.patch +Patch0014: 0014-rules-disable-auto-online-of-hot-plugged-memory-on-I.patch +Patch0015: 0015-rules-introduce-old-style-by-path-symlinks-for-FCP-b.patch +Patch0016: 0016-Revert-udev-remove-WAIT_FOR-key.patch +Patch0017: 0017-net_setup_link-allow-renaming-interfaces-that-were-r.patch +Patch0018: 0018-units-drop-DynamicUser-yes-from-systemd-resolved.ser.patch +Patch0019: 0019-journal-remove-journal-audit-socket.patch +Patch0020: 0020-bus-move-BUS_DONT_DESTROY-calls-after-asserts.patch +Patch0021: 0021-random-seed-raise-POOL_SIZE_MIN-constant-to-1024.patch +Patch0022: 0022-cryptsetup-add-support-for-sector-size-option-9936.patch +Patch0023: 0023-cryptsetup-do-not-define-arg_sector_size-if-libgcryp.patch +Patch0024: 0024-units-don-t-enable-per-service-IP-firewall-by-defaul.patch +Patch0025: 0025-bus-message-do-not-crash-on-message-with-a-string-of.patch +Patch0026: 0026-Introduce-free_and_strndup-and-use-it-in-bus-message.patch +Patch0027: 0027-tests-backport-test_setup_logging.patch +Patch0028: 0028-journal-change-support-URL-shown-in-the-catalog-entr.patch +Patch0029: 0029-resolved-create-etc-resolv.conf-symlink-at-runtime.patch +Patch0030: 0030-dissect-image-use-right-comparison-function.patch +Patch0031: 0031-login-avoid-leak-of-name-returned-by-uid_to_name.patch +Patch0032: 0032-firewall-util-add-an-assert-that-we-re-not-overwriti.patch +Patch0033: 0033-journal-file-avoid-calling-ftruncate-with-invalid-fd.patch +Patch0034: 0034-dhcp6-make-sure-we-have-enough-space-for-the-DHCP6-o.patch +Patch0035: 0035-core-rename-queued_message-pending_reload_message.patch +Patch0036: 0036-core-when-we-can-t-send-the-pending-reload-message-s.patch +Patch0037: 0037-core-make-sure-we-don-t-throttle-change-signal-gener.patch +Patch0038: 0038-proc-cmdline-introduce-PROC_CMDLINE_RD_STRICT.patch +Patch0039: 0039-debug-generator-introduce-rd.-version-of-all-options.patch +Patch0040: 0040-chown-recursive-let-s-rework-the-recursive-logic-to-.patch +Patch0041: 0041-chown-recursive-also-drop-ACLs-when-recursively-chow.patch +Patch0042: 0042-chown-recursive-TAKE_FD-is-your-friend.patch +Patch0043: 0043-test-add-test-case-for-recursive-chown-ing.patch +Patch0044: 0044-Revert-sysctl.d-request-ECN-on-both-in-and-outgoing-.patch +Patch0045: 0045-detect-virt-do-not-try-to-read-all-of-proc-cpuinfo.patch +Patch0046: 0046-sd-bus-unify-three-code-paths-which-free-struct-bus_.patch +Patch0047: 0047-sd-bus-properly-initialize-containers.patch +Patch0048: 0048-cryptsetup-generator-introduce-basic-keydev-support.patch +Patch0049: 0049-cryptsetup-don-t-use-m-if-there-s-no-error-to-show.patch +Patch0050: 0050-cryptsetup-generator-don-t-return-error-if-target-di.patch +Patch0051: 0051-cryptsetup-generator-allow-whitespace-characters-in-.patch +Patch0052: 0052-rules-watch-metadata-changes-on-DASD-devices.patch +Patch0053: 0053-sysctl.d-switch-net.ipv4.conf.all.rp_filter-from-1-t.patch +Patch0054: 0054-tests-explicitly-enable-user-namespaces-for-TEST-13-.patch +Patch0055: 0055-nspawn-beef-up-netns-checking-a-bit-for-compat-with-.patch +Patch0056: 0056-test-Drop-SKIP_INITRD-for-QEMU-based-tests.patch +Patch0057: 0057-meson-rename-Ddebug-to-Ddebug-extra.patch +Patch0058: 0058-meson-check-whether-gnutls-supports-TCP-fast-open.patch +Patch0059: 0059-unit-don-t-add-Requires-for-tmp.mount.patch +Patch0060: 0060-tests-drop-the-precondition-check-for-inherited-flag.patch +Patch0061: 0061-core-when-deserializing-state-always-use-read_line-L.patch +Patch0062: 0062-core-enforce-a-limit-on-STATUS-texts-recvd-from-serv.patch +Patch0063: 0063-travis-enable-Travis-CI-on-CentOS-7.patch +Patch0064: 0064-travis-RHEL8-support.patch +Patch0065: 0065-travis-drop-the-SELinux-Fedora-workaround.patch +Patch0066: 0066-travis-fix-syntax-error-in-.travis.yml.patch +Patch0067: 0067-travis-reboot-the-container-before-running-tests.patch +Patch0068: 0068-coredump-remove-duplicate-MESSAGE-prefix-from-messag.patch +Patch0069: 0069-journald-remove-unnecessary.patch +Patch0070: 0070-journald-do-not-store-the-iovec-entry-for-process-co.patch +Patch0071: 0071-basic-process-util-limit-command-line-lengths-to-_SC.patch +Patch0072: 0072-coredump-fix-message-when-we-fail-to-save-a-journald.patch +Patch0073: 0073-procfs-util-expose-functionality-to-query-total-memo.patch +Patch0074: 0074-basic-prioq-add-prioq_peek_item.patch +Patch0075: 0075-journal-limit-the-number-of-entries-in-the-cache-bas.patch +Patch0076: 0076-journald-periodically-drop-cache-for-all-dead-PIDs.patch +Patch0077: 0077-process-util-don-t-use-overly-large-buffer-to-store-.patch +Patch0078: 0078-Revert-sysctl.d-switch-net.ipv4.conf.all.rp_filter-f.patch +Patch0079: 0079-journal-fix-syslog_parse_identifier.patch +Patch0080: 0080-journald-set-a-limit-on-the-number-of-fields-1k.patch +Patch0081: 0081-journald-when-processing-a-native-message-bail-more-.patch +Patch0082: 0082-journald-lower-the-maximum-entry-size-limit-to-for-n.patch +Patch0083: 0083-httpd-use-a-cleanup-function-to-call-MHD_destroy_res.patch +Patch0084: 0084-journal-remote-verify-entry-length-from-header.patch +Patch0085: 0085-journal-remote-set-a-limit-on-the-number-of-fields-i.patch +Patch0086: 0086-journald-correctly-attribute-log-messages-also-with-.patch +Patch0087: 0087-test-replace-echo-with-socat.patch +Patch0088: 0088-test-network-ignore-tunnel-devices-automatically-add.patch +Patch0089: 0089-rules-add-elevator-kernel-command-line-parameter.patch +Patch0090: 0090-rule-syntax-check-allow-PROGRAM-as-an-assignment.patch +Patch0091: 0091-rules-implement-new-memory-hotplug-policy.patch +Patch0092: 0092-LGTM-make-LGTM.com-use-meson-from-pip.patch +Patch0093: 0093-lgtm-use-python3.patch +Patch0094: 0094-tools-use-print-function-in-Python-3-code.patch +Patch0095: 0095-lgtm-add-a-custom-query-for-catching-the-use-of-fget.patch +Patch0096: 0096-lgtm-drop-redundant-newlines.patch +Patch0097: 0097-rules-add-the-rule-that-adds-elevator-kernel-command.patch +Patch0098: 0098-test-add-TEST-24-UNIT-TESTS-running-all-basic-tests-.patch +Patch0099: 0099-tests-create-the-asan-wrapper-automatically-if-syste.patch +Patch0100: 0100-tests-add-a-wrapper-for-when-systemd-is-built-with-A.patch +Patch0101: 0101-tests-redirect-ASAN-reports-on-journald-to-a-file.patch +Patch0102: 0102-tests-use-the-asan-wrapper-to-boot-a-VM-container-if.patch +Patch0103: 0103-tests-allow-passing-additional-arguments-to-nspawn-v.patch +Patch0104: 0104-tests-also-run-TEST-01-BASIC-in-an-unprivileged-cont.patch +Patch0105: 0105-test-don-t-overwrite-TESTDIR-if-already-set.patch +Patch0106: 0106-bus-socket-Fix-line_begins-to-accept-word-matching-f.patch +Patch0107: 0107-Refuse-dbus-message-paths-longer-than-BUS_PATH_SIZE_.patch +Patch0108: 0108-Allocate-temporary-strings-to-hold-dbus-paths-on-the.patch +Patch0109: 0109-sd-bus-if-we-receive-an-invalid-dbus-message-ignore-.patch +Patch0110: 0110-meson-drop-misplaced-Wl-undefined-argument.patch +Patch0111: 0111-Revert-core-one-step-back-again-for-nspawn-we-actual.patch +Patch0112: 0112-tree-wide-shorten-error-logging-a-bit.patch +Patch0113: 0113-nspawn-simplify-machine-terminate-bus-call.patch +Patch0114: 0114-nspawn-merge-two-variable-declaration-lines.patch +Patch0115: 0115-nspawn-rework-how-we-allocate-kill-scopes.patch +Patch0116: 0116-unit-enqueue-cgroup-empty-check-event-if-the-last-re.patch +Patch0117: 0117-Revert-journal-remove-journal-audit-socket.patch +Patch0118: 0118-journal-don-t-enable-systemd-journald-audit.socket-b.patch +Patch0119: 0119-logs-show-use-grey-color-for-de-emphasizing-journal-.patch +Patch0120: 0120-units-add-Install-section-to-tmp.mount.patch +Patch0121: 0121-nss-do-not-modify-errno-when-NSS_STATUS_NOTFOUND-or-.patch +Patch0122: 0122-util.h-add-new-UNPROTECT_ERRNO-macro.patch +Patch0123: 0123-nss-unportect-errno-before-writing-to-NSS-errnop.patch +Patch0124: 0124-seccomp-reduce-logging-about-failure-to-add-syscall-.patch +Patch0125: 0125-format-table-when-duplicating-a-cell-also-copy-the-c.patch +Patch0126: 0126-format-table-optionally-make-specific-cells-clickabl.patch +Patch0127: 0127-format-table-before-outputting-a-color-check-if-colo.patch +Patch0128: 0128-format-table-add-option-to-store-format-percent-and-.patch +Patch0129: 0129-format-table-optionally-allow-reversing-the-sort-ord.patch +Patch0130: 0130-format-table-add-table_update-to-update-existing-ent.patch +Patch0131: 0131-format-table-add-an-API-for-getting-the-cell-at-a-sp.patch +Patch0132: 0132-format-table-always-underline-header-line.patch +Patch0133: 0133-format-table-add-calls-to-query-the-data-in-a-specif.patch +Patch0134: 0134-format-table-make-sure-we-never-call-memcmp-with-NUL.patch +Patch0135: 0135-format-table-use-right-field-for-display.patch +Patch0136: 0136-format-table-add-option-to-uppercase-cells-on-displa.patch +Patch0137: 0137-format-table-never-try-to-reuse-cells-that-have-colo.patch +Patch0138: 0138-locale-util-add-logic-to-output-smiley-emojis-at-var.patch +Patch0139: 0139-analyze-add-new-security-verb.patch +Patch0140: 0140-tests-add-a-rudimentary-fuzzer-for-server_process_sy.patch +Patch0141: 0141-journald-make-it-clear-that-dev_kmsg_record-modifies.patch +Patch0142: 0142-journald-free-the-allocated-memory-before-returning-.patch +Patch0143: 0143-tests-rework-the-code-fuzzing-journald.patch +Patch0144: 0144-journald-make-server_process_native_message-compatib.patch +Patch0145: 0145-tests-add-a-fuzzer-for-server_process_native_message.patch +Patch0146: 0146-tests-add-a-fuzzer-for-sd-ndisc.patch +Patch0147: 0147-ndisc-fix-two-infinite-loops.patch +Patch0148: 0148-tests-add-reproducers-for-several-issues-uncovered-w.patch +Patch0149: 0149-tests-add-a-reproducer-for-an-infinite-loop-in-ndisc.patch +Patch0150: 0150-tests-add-a-reproducer-for-another-infinite-loop-in-.patch +Patch0151: 0151-fuzz-rename-fuzz-corpus-directory-to-just-fuzz.patch +Patch0152: 0152-test-add-testcase-for-issue-10007-by-oss-fuzz.patch +Patch0153: 0153-fuzz-unify-the-fuzz-regressions-directory-with-the-m.patch +Patch0154: 0154-test-bus-marshal-use-cescaping-instead-of-hexmem.patch +Patch0155: 0155-meson-add-Dlog-trace-to-set-LOG_TRACE.patch +Patch0156: 0156-meson-allow-building-resolved-and-machined-without-n.patch +Patch0157: 0157-meson-drop-duplicated-condition.patch +Patch0158: 0158-meson-use-.source_root-in-more-places.patch +Patch0159: 0159-meson-treat-all-fuzz-cases-as-unit-tests.patch +Patch0160: 0160-fuzz-bus-message-add-fuzzer-for-message-parsing.patch +Patch0161: 0161-bus-message-use-structured-initialization-to-avoid-u.patch +Patch0162: 0162-bus-message-avoid-an-infinite-loop-on-empty-structur.patch +Patch0163: 0163-bus-message-let-s-always-use-EBADMSG-when-the-messag.patch +Patch0164: 0164-bus-message-rename-function-for-clarity.patch +Patch0165: 0165-bus-message-use-define.patch +Patch0166: 0166-bus-do-not-print-null-if-the-message-has-unknown-typ.patch +Patch0167: 0167-bus-message-fix-calculation-of-offsets-table.patch +Patch0168: 0168-bus-message-remove-duplicate-assignment.patch +Patch0169: 0169-bus-message-fix-calculation-of-offsets-table-for-arr.patch +Patch0170: 0170-bus-message-drop-asserts-in-functions-which-are-wrap.patch +Patch0171: 0171-bus-message-output-debug-information-about-offset-tr.patch +Patch0172: 0172-bus-message-fix-skipping-of-array-fields-in-gvariant.patch +Patch0173: 0173-bus-message-also-properly-copy-struct-signature-when.patch +Patch0174: 0174-fuzz-bus-message-add-two-test-cases-that-pass-now.patch +Patch0175: 0175-bus-message-return-EBADMSG-not-EINVAL-on-invalid-gva.patch +Patch0176: 0176-bus-message-avoid-wrap-around-when-using-length-read.patch +Patch0177: 0177-util-do-not-use-stack-frame-for-parsing-arbitrary-in.patch +Patch0178: 0178-travis-enable-ASan-and-UBSan-on-RHEL8.patch +Patch0179: 0179-tests-keep-SYS_PTRACE-when-running-under-ASan.patch +Patch0180: 0180-tree-wide-various-ubsan-zero-size-memory-fixes.patch +Patch0181: 0181-util-introduce-memcmp_safe.patch +Patch0182: 0182-test-socket-util-avoid-memleak-reported-by-valgrind.patch +Patch0183: 0183-sd-journal-escape-binary-data-in-match_make_string.patch +Patch0184: 0184-capability-introduce-CAP_TO_MASK_CORRECTED-macro-rep.patch +Patch0185: 0185-sd-bus-use-size_t-when-dealing-with-memory-offsets.patch +Patch0186: 0186-sd-bus-call-cap_last_cap-only-once-in-has_cap.patch +Patch0187: 0187-mount-point-honour-AT_SYMLINK_FOLLOW-correctly.patch +Patch0188: 0188-travis-switch-from-trusty-to-xenial.patch +Patch0189: 0189-test-socket-util-Add-tests-for-receive_fd_iov-and-fr.patch +Patch0190: 0190-socket-util-Introduce-send_one_fd_iov-and-receive_on.patch +Patch0191: 0191-core-swap-order-of-n_storage_fds-and-n_socket_fds-pa.patch +Patch0192: 0192-execute-use-our-usual-syntax-for-defining-bit-masks.patch +Patch0193: 0193-core-introduce-new-Type-exec-service-type.patch +Patch0194: 0194-man-document-the-new-Type-exec-type.patch +Patch0195: 0195-sd-bus-allow-connecting-to-the-pseudo-container-.hos.patch +Patch0196: 0196-sd-login-let-s-also-make-sd-login-understand-.host.patch +Patch0197: 0197-test-add-test-for-Type-exec.patch +Patch0198: 0198-journal-gateway-explicitly-declare-local-variables.patch +Patch0199: 0199-tools-drop-unused-variable.patch +Patch0200: 0200-journal-gateway-use-localStorage-cursor-only-when-it.patch +Patch0201: 0201-sd-bus-deal-with-cookie-overruns.patch +Patch0202: 0202-journal-remote-do-not-request-Content-Length-if-Tran.patch +Patch0203: 0203-journal-do-not-remove-multiple-spaces-after-identifi.patch +Patch0204: 0204-cryptsetup-Do-not-fallback-to-PLAIN-mapping-if-LUKS-.patch +Patch0205: 0205-cryptsetup-call-crypt_load-for-LUKS-only-once.patch +Patch0206: 0206-cryptsetup-Add-LUKS2-token-support.patch +Patch0207: 0207-udev-scsi_id-fix-incorrect-page-length-when-get-devi.patch +Patch0208: 0208-Change-job-mode-of-manager-triggered-restarts-to-JOB.patch +Patch0209: 0209-bash-completion-analyze-support-security.patch +Patch0210: 0210-man-note-that-journal-does-not-validate-syslog-field.patch +Patch0211: 0211-rules-skip-memory-hotplug-on-ppc64.patch +Patch0212: 0212-mount-simplify-proc-self-mountinfo-handler.patch +Patch0213: 0213-mount-rescan-proc-self-mountinfo-before-processing-w.patch +Patch0214: 0214-swap-scan-proc-swaps-before-processing-waitid-result.patch +Patch0215: 0215-analyze-security-fix-potential-division-by-zero.patch +Patch0216: 0216-core-never-propagate-reload-failure-to-service-resul.patch +Patch0217: 0217-man-document-systemd-analyze-security.patch +Patch0218: 0218-man-reorder-and-add-examples-to-systemd-analyze-1.patch +Patch0219: 0219-travis-move-to-CentOS-8-docker-images.patch +Patch0220: 0220-travis-drop-SCL-remains.patch +Patch0221: 0221-syslog-fix-segfault-in-syslog_parse_priority.patch +Patch0222: 0222-sd-bus-make-strict-asan-shut-up.patch +Patch0223: 0223-travis-don-t-run-slow-tests-under-ASan-UBSan.patch +Patch0224: 0224-kernel-install-do-not-require-non-empty-kernel-cmdli.patch +Patch0225: 0225-ask-password-prevent-buffer-overrow-when-reading-fro.patch +Patch0226: 0226-core-try-to-reopen-dev-kmsg-again-right-after-mounti.patch +Patch0227: 0227-buildsys-don-t-garbage-collect-sections-while-linkin.patch +Patch0228: 0228-udev-introduce-CONST-key-name.patch +Patch0229: 0229-Call-getgroups-to-know-size-of-supplementary-groups-.patch +Patch0230: 0230-Consider-smb3-as-remote-filesystem.patch +Patch0231: 0231-process-util-introduce-pid_is_my_child-helper.patch +Patch0232: 0232-core-reduce-the-number-of-stalled-PIDs-from-the-watc.patch +Patch0233: 0233-core-only-watch-processes-when-it-s-really-necessary.patch +Patch0234: 0234-core-implement-per-unit-journal-rate-limiting.patch +Patch0235: 0235-path-stop-watching-path-specs-once-we-triggered-the-.patch +Patch0236: 0236-journald-fixed-assertion-failure-when-system-journal.patch +Patch0237: 0237-test-use-PBKDF2-instead-of-Argon2-in-cryptsetup.patch +Patch0238: 0238-test-mask-several-unnecessary-services.patch +Patch0239: 0239-test-bump-the-second-partition-s-size-to-50M.patch +Patch0240: 0240-shared-sleep-config-exclude-zram-devices-from-hibern.patch +Patch0241: 0241-selinux-don-t-log-SELINUX_INFO-and-SELINUX_WARNING-m.patch +Patch0242: 0242-sd-device-introduce-log_device_-macros.patch +Patch0243: 0243-udev-Add-id-program-and-rule-for-FIDO-security-token.patch +Patch0244: 0244-shared-but-util-drop-trusted-annotation-from-bus_ope.patch +Patch0245: 0245-sd-bus-adjust-indentation-of-comments.patch +Patch0246: 0246-resolved-do-not-run-loop-twice.patch +Patch0247: 0247-resolved-allow-access-to-Set-Link-and-Revert-methods.patch +Patch0248: 0248-resolved-query-polkit-only-after-parsing-the-data.patch +Patch0249: 0249-journal-rely-on-_cleanup_free_-to-free-a-temporary-s.patch +Patch0250: 0250-basic-user-util-allow-dots-in-user-names.patch +Patch0251: 0251-sd-bus-bump-message-queue-size-again.patch +Patch0252: 0252-tests-put-fuzz_journald_processing_function-in-a-.c-.patch +Patch0253: 0253-tests-add-a-fuzzer-for-dev_kmsg_record.patch +Patch0254: 0254-basic-remove-an-assertion-from-cunescape_one.patch +Patch0255: 0255-journal-fix-an-off-by-one-error-in-dev_kmsg_record.patch +Patch0256: 0256-tests-add-a-reproducer-for-a-memory-leak-fixed-in-30.patch +Patch0257: 0257-tests-add-a-reproducer-for-a-heap-buffer-overflow-fi.patch +Patch0258: 0258-test-initialize-syslog_fd-in-fuzz-journald-kmsg-too.patch +Patch0259: 0259-tests-add-a-fuzzer-for-process_audit_string.patch +Patch0260: 0260-journald-check-whether-sscanf-has-changed-the-value-.patch +Patch0261: 0261-tests-introduce-dummy_server_init-and-use-it-in-all-.patch +Patch0262: 0262-tests-add-a-fuzzer-for-journald-streams.patch +Patch0263: 0263-tests-add-a-fuzzer-for-server_process_native_file.patch +Patch0264: 0264-fuzz-journal-stream-avoid-assertion-failure-on-sampl.patch +Patch0265: 0265-journald-take-leading-spaces-into-account-in-syslog_.patch +Patch0266: 0266-Add-a-warning-about-the-difference-in-permissions-be.patch +Patch0267: 0267-execute-remove-one-redundant-comparison-check.patch +Patch0268: 0268-core-change-ownership-mode-of-the-execution-director.patch +Patch0269: 0269-core-dbus-execute-remove-unnecessary-initialization.patch +Patch0270: 0270-shared-cpu-set-util-move-the-part-to-print-cpu-set-i.patch +Patch0271: 0271-shared-cpu-set-util-remove-now-unused-CPU_SIZE_TO_NU.patch +Patch0272: 0272-Rework-cpu-affinity-parsing.patch +Patch0273: 0273-Move-cpus_in_affinity_mask-to-cpu-set-util.-ch.patch +Patch0274: 0274-test-cpu-set-util-add-simple-test-for-cpus_in_affini.patch +Patch0275: 0275-test-cpu-set-util-add-a-smoke-test-for-test_parse_cp.patch +Patch0276: 0276-pid1-parse-CPUAffinity-in-incremental-fashion.patch +Patch0277: 0277-pid1-don-t-reset-setting-from-proc-cmdline-upon-rest.patch +Patch0278: 0278-pid1-when-reloading-configuration-forget-old-setting.patch +Patch0279: 0279-test-execute-use-CPUSet-too.patch +Patch0280: 0280-shared-cpu-set-util-drop-now-unused-cleanup-function.patch +Patch0281: 0281-shared-cpu-set-util-make-transfer-of-cpu_set_t-over-.patch +Patch0282: 0282-test-cpu-set-util-add-test-for-dbus-conversions.patch +Patch0283: 0283-shared-cpu-set-util-introduce-cpu_set_to_range.patch +Patch0284: 0284-systemctl-present-CPUAffinity-mask-as-a-list-of-CPU-.patch +Patch0285: 0285-shared-cpu-set-util-only-force-range-printing-one-ti.patch +Patch0286: 0286-execute-dump-CPUAffinity-as-a-range-string-instead-o.patch +Patch0287: 0287-cpu-set-util-use-d-d-format-in-cpu_set_to_range_stri.patch +Patch0288: 0288-core-introduce-NUMAPolicy-and-NUMAMask-options.patch +Patch0289: 0289-core-disable-CPUAccounting-by-default.patch +Patch0290: 0290-set-kptr_restrict-1.patch +Patch0291: 0291-cryptsetup-reduce-the-chance-that-we-will-be-OOM-kil.patch +Patch0292: 0292-core-job-fix-breakage-of-ordering-dependencies-by-sy.patch +Patch0293: 0293-debug-generator-enable-custom-systemd.debug_shell-tt.patch +Patch0294: 0294-test-cpu-set-util-fix-comparison-for-allocation-size.patch +Patch0295: 0295-test-cpu-set-util-fix-allocation-size-check-on-i386.patch +Patch0296: 0296-catalog-fix-name-of-variable.patch +Patch0297: 0297-cryptsetup-add-keyfile-timeout-to-allow-a-keydev-tim.patch +Patch0298: 0298-cryptsetup-add-documentation-for-keyfile-timeout.patch +Patch0299: 0299-cryptsetup-use-unabbrieviated-variable-names.patch +Patch0300: 0300-cryptsetup-don-t-assert-on-variable-which-is-optiona.patch +Patch0301: 0301-cryptsetup-generator-guess-whether-the-keyfile-argum.patch +Patch0302: 0302-crypt-util-Translate-libcryptsetup-log-level-instead.patch +Patch0303: 0303-cryptsetup-add-some-commenting-about-EAGAIN-generati.patch +Patch0304: 0304-cryptsetup-downgrade-a-log-message-we-ignore.patch +Patch0305: 0305-cryptsetup-rework-how-we-log-about-activation-failur.patch +Patch0306: 0306-rules-reintroduce-60-alias-kmsg.rules.patch +Patch0307: 0307-sd-bus-make-rqueue-wqueue-sizes-of-type-size_t.patch +Patch0308: 0308-sd-bus-reorder-bus-ref-and-bus-message-ref-handling.patch +Patch0309: 0309-sd-bus-make-sure-dispatch_rqueue-initializes-return-.patch +Patch0310: 0310-sd-bus-drop-two-inappropriate-empty-lines.patch +Patch0311: 0311-sd-bus-initialize-mutex-after-we-allocated-the-wqueu.patch +Patch0312: 0312-sd-bus-always-go-through-sd_bus_unref-to-free-messag.patch +Patch0313: 0313-bus-message-introduce-two-kinds-of-references-to-bus.patch +Patch0314: 0314-sd-bus-introduce-API-for-re-enqueuing-incoming-messa.patch +Patch0315: 0315-sd-event-add-sd_event_source_disable_unref-helper.patch +Patch0316: 0316-polkit-when-authorizing-via-PK-let-s-re-resolve-call.patch +Patch0317: 0317-sysctl-let-s-by-default-increase-the-numeric-PID-ran.patch +Patch0318: 0318-journal-do-not-trigger-assertion-when-journal_file_c.patch +Patch0319: 0319-journal-use-cleanup-attribute-at-one-more-place.patch +Patch0320: 0320-sd-bus-use-queue-message-references-for-managing-r-w.patch +Patch0321: 0321-pid1-make-sure-to-restore-correct-default-values-for.patch +Patch0322: 0322-main-introduce-a-define-HIGH_RLIMIT_MEMLOCK-similar-.patch +Patch0323: 0323-seccomp-introduce-seccomp_restrict_suid_sgid-for-blo.patch +Patch0324: 0324-test-add-test-case-for-restrict_suid_sgid.patch +Patch0325: 0325-core-expose-SUID-SGID-restriction-as-new-unit-settin.patch +Patch0326: 0326-analyze-check-for-RestrictSUIDSGID-in-systemd-analyz.patch +Patch0327: 0327-man-document-the-new-RestrictSUIDSGID-setting.patch +Patch0328: 0328-units-turn-on-RestrictSUIDSGID-in-most-of-our-long-r.patch +Patch0329: 0329-core-imply-NNP-and-SUID-SGID-restriction-for-Dynamic.patch +Patch0330: 0330-cgroup-introduce-support-for-cgroup-v2-CPUSET-contro.patch +Patch0331: 0331-pid1-fix-DefaultTasksMax-initialization.patch +Patch0332: 0332-cgroup-make-sure-that-cpuset-is-supported-on-cgroup-.patch +Patch0333: 0333-test-introduce-TEST-36-NUMAPOLICY.patch +Patch0334: 0334-test-replace-tail-f-with-journal-cursor-which-should.patch +Patch0335: 0335-test-support-MPOL_LOCAL-matching-in-unpatched-strace.patch +Patch0336: 0336-test-make-sure-the-strace-process-is-indeed-dead.patch +Patch0337: 0337-test-skip-the-test-on-systems-without-NUMA-support.patch +Patch0338: 0338-test-give-strace-some-time-to-initialize.patch +Patch0339: 0339-test-add-a-simple-sanity-check-for-systems-without-N.patch +Patch0340: 0340-test-drop-the-missed-exit-1-expression.patch +Patch0341: 0341-test-replace-cursor-file-with-a-plain-cursor.patch +Patch0342: 0342-cryptsetup-Treat-key-file-errors-as-a-failed-passwor.patch +Patch0343: 0343-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch +Patch0344: 0344-resolved-Recover-missing-PrivateTmp-yes-and-ProtectS.patch +Patch0345: 0345-bus_open-leak-sd_event_source-when-udevadm-trigger.patch +Patch0346: 0346-core-rework-StopWhenUnneeded-logic.patch +Patch0347: 0347-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch +Patch0348: 0348-core-fix-re-realization-of-cgroup-siblings.patch +Patch0349: 0349-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch +Patch0350: 0350-core-transition-to-FINAL_SIGTERM-state-after-ExecSto.patch +Patch0351: 0351-sd-journal-close-journal-files-that-were-deleted-by-.patch +Patch0352: 0352-sd-journal-remove-the-dead-code-and-actually-fix-146.patch +Patch0353: 0353-udev-downgrade-message-when-we-fail-to-set-inotify-w.patch +Patch0354: 0354-logind-check-PolicyKit-before-allowing-VT-switch.patch +Patch0355: 0355-test-do-not-use-global-variable-to-pass-error.patch +Patch0356: 0356-test-install-libraries-required-by-tests.patch +Patch0357: 0357-test-introduce-install_zoneinfo.patch +Patch0358: 0358-test-replace-duplicated-Makefile-by-symbolic-link.patch +Patch0359: 0359-test-add-paths-of-keymaps-in-install_keymaps.patch +Patch0360: 0360-test-make-install_keymaps-optionally-install-more-ke.patch +Patch0361: 0361-test-fs-util-skip-some-tests-when-running-in-unprivi.patch +Patch0362: 0362-test-process-util-skip-several-verifications-when-ru.patch +Patch0363: 0363-test-execute-also-check-python3-is-installed-or-not.patch +Patch0364: 0364-test-execute-skip-several-tests-when-running-in-cont.patch +Patch0365: 0365-test-introduce-test_is_running_from_builddir.patch +Patch0366: 0366-test-make-test-catalog-relocatable.patch +Patch0367: 0367-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch +Patch0368: 0368-test-try-to-determine-QEMU_SMP-dynamically.patch +Patch0369: 0369-test-store-coredumps-in-journal.patch +Patch0370: 0370-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch +Patch0371: 0371-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch +Patch0372: 0372-man-be-clearer-that-.timer-time-expressions-need-to-.patch +Patch0373: 0373-Add-support-for-opening-files-for-appending.patch +Patch0374: 0374-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch +Patch0375: 0375-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch +Patch0376: 0376-core-move-unit_status_emit_starting_stopping_reloadi.patch +Patch0377: 0377-job-when-a-job-was-skipped-due-to-a-failed-condition.patch +Patch0378: 0378-core-split-out-all-logic-that-updates-a-Job-on-a-uni.patch +Patch0379: 0379-core-make-log-messages-about-units-entering-a-failed.patch +Patch0380: 0380-core-log-a-recognizable-message-when-a-unit-succeeds.patch +Patch0381: 0381-tests-always-use-the-right-vtable-wrapper-calls.patch +Patch0382: 0382-test-execute-allow-filtering-test-cases-by-pattern.patch +Patch0383: 0383-test-execute-provide-custom-failure-message.patch +Patch0384: 0384-core-ExecCondition-for-services.patch +Patch0385: 0385-Drop-support-for-lz4-1.3.0.patch +Patch0386: 0386-test-compress-add-test-for-short-decompress_startswi.patch +Patch0387: 0387-journal-adapt-for-new-improved-LZ4_decompress_safe_p.patch +Patch0388: 0388-fuzz-compress-add-fuzzer-for-compression-and-decompr.patch +Patch0389: 0389-seccomp-fix-__NR__sysctl-usage.patch +Patch0390: 0390-tmpfiles-fix-crash-with-NULL-in-arg_root-and-other-f.patch +Patch0391: 0391-sulogin-shell-Use-force-if-SYSTEMD_SULOGIN_FORCE-set.patch +Patch0392: 0392-resolvconf-fixes-for-the-compatibility-interface.patch +Patch0393: 0393-mount-don-t-add-Requires-for-tmp.mount.patch +Patch0394: 0394-core-coldplug-possible-nop_job.patch +Patch0395: 0395-core-add-IODeviceLatencyTargetSec.patch +Patch0396: 0396-time-util-Introduce-parse_sec_def_infinity.patch +Patch0397: 0397-cgroup-use-structured-initialization.patch +Patch0398: 0398-core-add-CPUQuotaPeriodSec.patch +Patch0399: 0399-core-downgrade-CPUQuotaPeriodSec-clamping-logs-to-de.patch +Patch0400: 0400-sd-bus-avoid-magic-number-in-SASL-length-calculation.patch +Patch0401: 0401-sd-bus-fix-SASL-reply-to-empty-AUTH.patch +Patch0402: 0402-sd-bus-skip-sending-formatted-UIDs-via-SASL.patch +Patch0403: 0403-core-add-MemoryMin.patch +Patch0404: 0404-core-introduce-cgroup_add_device_allow.patch +Patch0405: 0405-test-remove-support-for-suffix-in-get_testdata_dir.patch +Patch0406: 0406-cgroup-Implement-default-propagation-of-MemoryLow-wi.patch +Patch0407: 0407-cgroup-Create-UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP.patch +Patch0408: 0408-unit-Add-DefaultMemoryMin.patch +Patch0409: 0409-cgroup-Polish-hierarchically-aware-protection-docs-a.patch +Patch0410: 0410-cgroup-Readd-some-plumbing-for-DefaultMemoryMin.patch +Patch0411: 0411-cgroup-Support-0-value-for-memory-protection-directi.patch +Patch0412: 0412-cgroup-Test-that-it-s-possible-to-set-memory-protect.patch +Patch0413: 0413-cgroup-Check-ancestor-memory-min-for-unified-memory-.patch +Patch0414: 0414-cgroup-Respect-DefaultMemoryMin-when-setting-memory..patch +Patch0415: 0415-cgroup-Mark-memory-protections-as-explicitly-set-in-.patch +Patch0416: 0416-meson-allow-setting-the-version-string-during-config.patch +Patch0417: 0417-core-don-t-consider-SERVICE_SKIP_CONDITION-for-abnor.patch +Patch0418: 0418-selinux-do-preprocessor-check-only-in-selinux-access.patch +Patch0419: 0419-basic-cgroup-util-introduce-cg_get_keyed_attribute_f.patch +Patch0420: 0420-shared-add-generic-logic-for-waiting-for-a-unit-to-e.patch +Patch0421: 0421-shared-fix-assert-call.patch +Patch0422: 0422-shared-Don-t-try-calling-NULL-callback-in-bus_wait_f.patch +Patch0423: 0423-shared-add-NULL-callback-check-in-one-more-place.patch +Patch0424: 0424-core-introduce-support-for-cgroup-freezer.patch +Patch0425: 0425-core-cgroup-fix-return-value-of-unit_cgorup_freezer_.patch +Patch0426: 0426-core-fix-the-return-value-in-order-to-make-sure-we-d.patch +Patch0427: 0427-test-add-test-for-cgroup-v2-freezer-support.patch +Patch0428: 0428-fix-mis-merge.patch +Patch0429: 0429-tests-sleep-a-bit-and-give-kernel-time-to-perform-th.patch +Patch0430: 0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch +Patch0431: 0431-device-don-t-emit-PropetiesChanged-needlessly.patch +Patch0432: 0432-units-add-generic-boot-complete.target.patch +Patch0433: 0433-man-document-new-boot-complete.target-unit.patch +Patch0434: 0434-core-make-sure-to-restore-the-control-command-id-too.patch +Patch0435: 0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch +Patch0436: 0436-logind-don-t-print-warning-when-user-.service-templa.patch +Patch0437: 0437-build-use-simple-project-version-in-pkgconfig-files.patch +Patch0438: 0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch +Patch0439: 0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch +Patch0440: 0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch +Patch0441: 0441-units-fix-systemd.special-man-page-reference-in-syst.patch +Patch0442: 0442-units-drop-reference-to-sushell-man-page.patch +Patch0443: 0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch +Patch0444: 0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch +Patch0445: 0445-systemctl-replace-switch-statement-by-table-of-struc.patch +Patch0446: 0446-systemctl-reindent-table.patch +Patch0447: 0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch +Patch0448: 0448-systemctl-clean-up-start_unit_one-error-handling.patch +Patch0449: 0449-systemctl-split-out-extra-args-generation-into-helpe.patch +Patch0450: 0450-systemctl-add-new-show-transaction-switch.patch +Patch0451: 0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch +Patch0452: 0452-man-document-the-new-systemctl-show-transaction-opti.patch +Patch0453: 0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch +Patch0454: 0454-core-remove-support-for-API-bus-started-outside-our-.patch +Patch0455: 0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch +Patch0456: 0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch +Patch0457: 0457-core-add-support-for-setting-CPUAffinity-to-special-.patch +Patch0458: 0458-basic-user-util-always-use-base-10-for-user-group-nu.patch +Patch0459: 0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch +Patch0460: 0460-basic-parse-util-add-safe_atoux64.patch +Patch0461: 0461-parse-util-allow-tweaking-how-to-parse-integers.patch +Patch0462: 0462-parse-util-allow-0-as-alternative-to-0-and-0.patch +Patch0463: 0463-parse-util-make-return-parameter-optional-in-safe_at.patch +Patch0464: 0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch +Patch0465: 0465-user-util-be-stricter-in-parse_uid.patch +Patch0466: 0466-strv-add-new-macro-STARTSWITH_SET.patch +Patch0467: 0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch +Patch0468: 0468-tests-beef-up-integer-parsing-tests.patch +Patch0469: 0469-shared-user-util-add-compat-forms-of-user-name-check.patch +Patch0470: 0470-shared-user-util-emit-a-warning-on-names-with-dots.patch +Patch0471: 0471-user-util-Allow-names-starting-with-a-digit.patch +Patch0472: 0472-shared-user-util-allow-usernames-with-dots-in-specif.patch +Patch0473: 0473-user-util-switch-order-of-checks-in-valid_user_group.patch +Patch0474: 0474-user-util-rework-how-we-validate-user-names.patch +Patch0475: 0475-man-mention-System-Administrator-s-Guide-in-systemct.patch +Patch0476: 0476-udev-introduce-udev-net_id-naming-schemes.patch +Patch0477: 0477-meson-make-net.naming-scheme-default-configurable.patch +Patch0478: 0478-man-describe-naming-schemes-in-a-new-man-page.patch +Patch0479: 0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch +Patch0480: 0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch +Patch0481: 0481-fix-typo-in-ProtectSystem-option.patch +Patch0482: 0482-remove-references-of-non-existent-man-pages.patch +Patch0483: 0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch +Patch0484: 0484-locale-util-add-new-helper-locale_is_installed.patch +Patch0485: 0485-test-add-test-case-for-locale_is_installed.patch +Patch0486: 0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch +Patch0487: 0487-install-allow-instantiated-units-to-be-enabled-via-p.patch +Patch0488: 0488-install-small-refactor-to-combine-two-function-calls.patch +Patch0489: 0489-test-fix-a-memleak.patch +Patch0490: 0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch +Patch0491: 0491-shared-install-fix-preset-operations-for-non-service.patch +Patch0492: 0492-introduce-setsockopt_int-helper.patch +Patch0493: 0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch +Patch0494: 0494-core-add-new-PassPacketInfo-socket-unit-property.patch +Patch0495: 0495-resolved-tweak-cmsg-calculation.patch +Patch0496: 0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch +Patch0497: 0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch +Patch0498: 0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch +Patch0499: 0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch +Patch0500: 0500-use-link-to-RHEL-8-docs.patch +Patch0501: 0501-cgroup-Also-set-blkio.bfq.weight.patch +Patch0502: 0502-units-make-sure-initrd-cleanup.service-terminates-be.patch +Patch0503: 0503-core-reload-SELinux-label-cache-on-daemon-reload.patch +Patch0504: 0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch +Patch0505: 0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch +Patch0506: 0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch +Patch0507: 0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch +Patch0508: 0508-udev-make-algorithm-that-selects-highest-priority-de.patch +Patch0509: 0509-test-create-dev-null-in-test-udev.pl.patch +Patch0510: 0510-test-missing-die.patch +Patch0511: 0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch +Patch0512: 0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch +Patch0513: 0513-udev-test-fix-test-skip-condition.patch +Patch0514: 0514-udev-test-fix-missing-directory-test-run.patch +Patch0515: 0515-udev-test-check-if-permitted-to-create-block-device-.patch +Patch0516: 0516-test-udev-add-a-testcase-of-too-long-line.patch +Patch0517: 0517-test-udev-use-proper-semantics-for-too-long-line-wit.patch +Patch0518: 0518-test-udev-add-more-tests-for-line-continuations-and-.patch +Patch0519: 0519-test-udev-add-more-tests-for-line-continuation.patch +Patch0520: 0520-test-udev-fix-alignment-and-drop-unnecessary-white-s.patch +Patch0521: 0521-test-udev-test.pl-cleanup-if-skipping-test.patch +Patch0522: 0522-test-add-test-cases-for-empty-string-match.patch +Patch0523: 0523-test-add-test-case-for-multi-matches-when-use.patch +Patch0524: 0524-udev-test-do-not-rely-on-mail-group-being-defined.patch +Patch0525: 0525-test-udev-test.pl-allow-multiple-devices-per-test.patch +Patch0526: 0526-test-udev-test.pl-create-rules-only-once.patch +Patch0527: 0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch +Patch0528: 0528-test-udev-test.pl-use-computed-devnode-name.patch +Patch0529: 0529-test-udev-test.pl-test-correctness-of-symlink-target.patch +Patch0530: 0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch +Patch0531: 0531-test-udev-test.pl-fix-wrong-test-descriptions.patch +Patch0532: 0532-test-udev-test.pl-last_rule-is-unsupported.patch +Patch0533: 0533-test-udev-test.pl-Make-some-tests-a-little-harder.patch +Patch0534: 0534-test-udev-test.pl-remove-bogus-rules-from-magic-subs.patch +Patch0535: 0535-test-udev-test.pl-merge-space-and-var-with-space-tes.patch +Patch0536: 0536-test-udev-test.pl-merge-import-parent-tests-into-one.patch +Patch0537: 0537-test-udev-test.pl-count-good-results.patch +Patch0538: 0538-tests-udev-test.pl-add-multiple-device-test.patch +Patch0539: 0539-test-udev-test.pl-add-repeat-count.patch +Patch0540: 0540-test-udev-test.pl-generator-for-large-list-of-block-.patch +Patch0541: 0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch +Patch0542: 0542-test-udev_test.pl-add-expected-good-count.patch +Patch0543: 0543-test-udev-test-gracefully-exit-when-imports-fail.patch +Patch0544: 0544-Revert-test-add-test-cases-for-empty-string-match-an.patch +Patch0545: 0545-test-sys-script.py-add-missing-DEVNAME-entries-to-ue.patch +Patch0546: 0546-sd-event-split-out-helper-functions-for-reshuffling-.patch +Patch0547: 0547-sd-event-split-out-enable-and-disable-codepaths-from.patch +Patch0548: 0548-sd-event-mention-that-two-debug-logged-events-are-ig.patch +Patch0549: 0549-sd-event-split-clock-data-allocation-out-of-sd_event.patch +Patch0550: 0550-sd-event-split-out-code-to-add-remove-timer-event-so.patch +Patch0551: 0551-sd-event-fix-delays-assert-brain-o-17790.patch +Patch0552: 0552-sd-event-let-s-suffix-last_run-last_log-with-_usec.patch +Patch0553: 0553-sd-event-refuse-running-default-event-loops-in-any-o.patch +Patch0554: 0554-sd-event-ref-event-loop-while-in-sd_event_prepare-ot.patch +Patch0555: 0555-sd-event-follow-coding-style-with-naming-return-para.patch +Patch0556: 0556-sd-event-remove-earliest_index-latest_index-into-com.patch +Patch0557: 0557-sd-event-update-state-at-the-end-in-event_source_ena.patch +Patch0558: 0558-sd-event-increase-n_enabled_child_sources-just-once.patch +Patch0559: 0559-sd-event-add-ability-to-ratelimit-event-sources.patch +Patch0560: 0560-test-add-ratelimiting-test.patch +Patch0561: 0561-core-prevent-excessive-proc-self-mountinfo-parsing.patch +Patch0562: 0562-udev-run-link_update-with-increased-retry-count-in-s.patch +Patch0563: 0563-pam-systemd-use-secure_getenv-rather-than-getenv.patch + + +%ifarch %{ix86} x86_64 aarch64 +%global have_gnu_efi 1 +%endif + +BuildRequires: gcc +BuildRequires: gcc-c++ +BuildRequires: libcap-devel +BuildRequires: libmount-devel +BuildRequires: pam-devel +BuildRequires: libselinux-devel +BuildRequires: audit-libs-devel +BuildRequires: cryptsetup-devel +BuildRequires: dbus-devel +BuildRequires: libacl-devel +BuildRequires: gobject-introspection-devel +BuildRequires: libblkid-devel +BuildRequires: xz-devel +BuildRequires: xz +BuildRequires: lz4-devel +BuildRequires: lz4 +BuildRequires: bzip2-devel +BuildRequires: libidn2-devel +BuildRequires: libcurl-devel +BuildRequires: kmod-devel +BuildRequires: elfutils-devel +BuildRequires: libgcrypt-devel +BuildRequires: libgpg-error-devel +BuildRequires: gnutls-devel +BuildRequires: libmicrohttpd-devel +BuildRequires: libxkbcommon-devel +BuildRequires: iptables-devel +BuildRequires: libxslt +BuildRequires: docbook-style-xsl +BuildRequires: pkgconfig +BuildRequires: gperf +BuildRequires: gawk +BuildRequires: tree +BuildRequires: python3-devel +BuildRequires: python3-lxml +BuildRequires: firewalld-filesystem +%if 0%{?have_gnu_efi} +BuildRequires: gnu-efi gnu-efi-devel +%endif +BuildRequires: libseccomp-devel +BuildRequires: git +BuildRequires: meson >= 0.43 +BuildRequires: gettext + +Requires(post): coreutils +Requires(post): sed +Requires(post): acl +Requires(post): grep +Requires(pre): coreutils +Requires(pre): /usr/bin/getent +Requires(pre): /usr/sbin/groupadd +Requires: dbus >= 1.9.18 +Requires: %{name}-pam = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} +Recommends: diffutils +Requires: util-linux +Recommends: libxkbcommon%{?_isa} +Provides: /bin/systemctl +Provides: /sbin/shutdown +Provides: syslog +Provides: systemd-units = %{version}-%{release} +Provides: systemd-rpm-macros = %{version}-%{release} +Obsoletes: system-setup-keyboard < 0.9 +Provides: system-setup-keyboard = 0.9 +# systemd-sysv-convert was removed in f20: https://fedorahosted.org/fpc/ticket/308 +Obsoletes: systemd-sysv < 206 +# self-obsoletes so that dnf will install new subpackages on upgrade (#1260394) +Obsoletes: %{name} < 229-5 +Provides: systemd-sysv = 206 +Conflicts: initscripts < 9.56.1 +%if 0%{?fedora} +Conflicts: fedora-release < 23-0.12 +%endif + +%description +systemd is a system and service manager that runs as PID 1 and starts +the rest of the system. It provides aggressive parallelization +capabilities, uses socket and D-Bus activation for starting services, +offers on-demand starting of daemons, keeps track of processes using +Linux control groups, maintains mount and automount points, and +implements an elaborate transactional dependency-based service control +logic. systemd supports SysV and LSB init scripts and works as a +replacement for sysvinit. Other parts of this package are a logging daemon, +utilities to control basic system configuration like the hostname, +date, locale, maintain a list of logged-in users, system accounts, +runtime directories and settings, and daemons to manage simple network +configuration, network time synchronization, log forwarding, and name +resolution. + +%package libs +Summary: systemd libraries +License: LGPLv2+ and MIT +Obsoletes: libudev < 183 +Obsoletes: systemd < 185-4 +Conflicts: systemd < 185-4 +Obsoletes: systemd-compat-libs < 230 +Obsoletes: nss-myhostname < 0.4 +Provides: nss-myhostname = 0.4 +Provides: nss-myhostname%{_isa} = 0.4 +Requires(post): coreutils +Requires(post): sed +Requires(post): grep +Requires(post): /usr/bin/getent + +%description libs +Libraries for systemd and udev. + +%package pam +Summary: systemd PAM module +Requires: %{name} = %{version}-%{release} + +%description pam +Systemd PAM module registers the session with systemd-logind. + +%package devel +Summary: Development headers for systemd +License: LGPLv2+ and MIT +Requires: %{name}-libs%{?_isa} = %{version}-%{release} +Provides: libudev-devel = %{version} +Provides: libudev-devel%{_isa} = %{version} +Obsoletes: libudev-devel < 183 +# Fake dependency to make sure systemd-pam is pulled into multilib (#1414153) +Requires: %{name}-pam = %{version}-%{release} + +%description devel +Development headers and auxiliary files for developing applications linking +to libudev or libsystemd. + +%package udev +Summary: Rule-based device node and kernel event manager +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +Requires(post): grep +Requires: kmod >= 18-4 +# obsolete parent package so that dnf will install new subpackage on upgrade (#1260394) +Obsoletes: %{name} < 229-5 +Provides: udev = %{version} +Provides: udev%{_isa} = %{version} +Obsoletes: udev < 183 +# https://bugzilla.redhat.com/show_bug.cgi?id=1408878 +Recommends: kbd +License: LGPLv2+ + +%description udev +This package contains systemd-udev and the rules and hardware database +needed to manage device nodes. This package is necessary on physical +machines and in virtual machines, but not in containers. + +%package container +# Name is the same as in Debian +Summary: Tools for containers and VMs +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +# obsolete parent package so that dnf will install new subpackage on upgrade (#1260394) +Obsoletes: %{name} < 229-5 +License: LGPLv2+ + +%description container +Systemd tools to spawn and manage containers and virtual machines. + +This package contains systemd-nspawn, machinectl, systemd-machined, +and systemd-importd. + +%package journal-remote +# Name is the same as in Debian +Summary: Tools to send journal events over the network +Requires: %{name}%{?_isa} = %{version}-%{release} +License: LGPLv2+ +Requires(pre): /usr/bin/getent +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +Requires: firewalld-filesystem +Provides: %{name}-journal-gateway = %{version}-%{release} +Provides: %{name}-journal-gateway%{_isa} = %{version}-%{release} +Obsoletes: %{name}-journal-gateway < 227-7 + +%description journal-remote +Programs to forward journal entries over the network, using encrypted HTTP, +and to write journal files from serialized journal contents. + +This package contains systemd-journal-gatewayd, +systemd-journal-remote, and systemd-journal-upload. + +%package tests +Summary: Internal unit tests for systemd +Requires: %{name}%{?_isa} = %{version}-%{release} +License: LGPLv2+ + +%description tests +"Installed tests" that are usually run as part of the build system. +They can be useful to test systemd internals. + +%prep +%autosetup %{?gitcommit:-n %{name}-%{gitcommit}} -S git_am + +%build +%define ntpvendor %(source /etc/os-release; echo ${ID}) +%{!?ntpvendor: echo 'NTP vendor zone is not set!'; exit 1} + +CONFIGURE_OPTS=( + -Dsysvinit-path=/etc/rc.d/init.d + -Drc-local=/etc/rc.d/rc.local + -Dntp-servers='0.%{ntpvendor}.pool.ntp.org 1.%{ntpvendor}.pool.ntp.org 2.%{ntpvendor}.pool.ntp.org 3.%{ntpvendor}.pool.ntp.org' + -Ddns-servers='' + -Ddev-kvm-mode=0666 + -Dkmod=true + -Dxkbcommon=true + -Dblkid=true + -Dseccomp=true + -Dima=true + -Dselinux=true + -Dapparmor=false + -Dpolkit=true + -Dxz=true + -Dzlib=true + -Dbzip2=true + -Dlz4=true + -Dpam=true + -Dacl=true + -Dsmack=true + -Dgcrypt=true + -Daudit=true + -Delfutils=true + -Dlibcryptsetup=true + -Delfutils=true + -Dqrencode=false + -Dgnutls=true + -Dmicrohttpd=true + -Dlibidn2=true + -Dlibiptc=true + -Dlibcurl=true + -Defi=true + -Dgnu-efi=%{?have_gnu_efi:true}%{?!have_gnu_efi:false} + -Dtpm=true + -Dhwdb=true + -Dsysusers=true + -Ddefault-kill-user-processes=false + -Dtests=unsafe + -Dinstall-tests=true + -Dtty-gid=5 + -Dusers-gid=100 + -Dnobody-user=nobody + -Dnobody-group=nobody + -Dsplit-usr=false + -Dsplit-bin=true + -Db_lto=false + -Dnetworkd=false + -Dtimesyncd=false + -Ddefault-hierarchy=legacy + -Dversion-tag=%{version}-%{release} +) + +# Don't ship /var/log/README. The relationship between journal and syslog should be documented +# in the official documentation. +sed -ie "/subdir('doc\/var-log')/d" meson.build + +%meson "${CONFIGURE_OPTS[@]}" +%meson_build + +if diff %{SOURCE1} %{_vpath_builddir}/triggers.systemd; then + echo -e "\n\n\nWARNING: triggers.systemd in Source1 is different!" + echo -e " cp %{_vpath_builddir}/triggers.systemd %{SOURCE1}\n\n\n" +fi + +%install +%meson_install + +# udev links +mkdir -p %{buildroot}/%{_sbindir} +ln -sf ../bin/udevadm %{buildroot}%{_sbindir}/udevadm + +# Compatiblity and documentation files +touch %{buildroot}/etc/crypttab +chmod 600 %{buildroot}/etc/crypttab + +# /etc/initab +install -Dm0644 -t %{buildroot}/etc/ %{SOURCE5} + +# /etc/sysctl.conf compat +install -Dm0644 %{SOURCE6} %{buildroot}/etc/sysctl.conf +ln -s ../sysctl.conf %{buildroot}/etc/sysctl.d/99-sysctl.conf + +# We create all wants links manually at installation time to make sure +# they are not owned and hence overriden by rpm after the user deleted +# them. +rm -r %{buildroot}%{_sysconfdir}/systemd/system/*.target.wants + +# Make sure these directories are properly owned +mkdir -p %{buildroot}%{system_unit_dir}/basic.target.wants +mkdir -p %{buildroot}%{system_unit_dir}/default.target.wants +mkdir -p %{buildroot}%{system_unit_dir}/dbus.target.wants +mkdir -p %{buildroot}%{system_unit_dir}/syslog.target.wants +mkdir -p %{buildroot}%{_localstatedir}/run +mkdir -p %{buildroot}%{_localstatedir}/log +touch %{buildroot}%{_localstatedir}/run/utmp +touch %{buildroot}%{_localstatedir}/log/{w,b}tmp + +# Make sure the user generators dir exists too +mkdir -p %{buildroot}%{pkgdir}/system-generators +mkdir -p %{buildroot}%{pkgdir}/user-generators + +# Create new-style configuration files so that we can ghost-own them +touch %{buildroot}%{_sysconfdir}/hostname +touch %{buildroot}%{_sysconfdir}/vconsole.conf +touch %{buildroot}%{_sysconfdir}/locale.conf +touch %{buildroot}%{_sysconfdir}/machine-id +touch %{buildroot}%{_sysconfdir}/machine-info +touch %{buildroot}%{_sysconfdir}/localtime +mkdir -p %{buildroot}%{_sysconfdir}/X11/xorg.conf.d +touch %{buildroot}%{_sysconfdir}/X11/xorg.conf.d/00-keyboard.conf + +# Make sure the shutdown/sleep drop-in dirs exist +mkdir -p %{buildroot}%{pkgdir}/system-shutdown/ +mkdir -p %{buildroot}%{pkgdir}/system-sleep/ + +# Make sure directories in /var exist +mkdir -p %{buildroot}%{_localstatedir}/lib/systemd/coredump +mkdir -p %{buildroot}%{_localstatedir}/lib/systemd/catalog +mkdir -p %{buildroot}%{_localstatedir}/lib/systemd/backlight +mkdir -p %{buildroot}%{_localstatedir}/lib/systemd/rfkill +mkdir -p %{buildroot}%{_localstatedir}/lib/systemd/linger +mkdir -p %{buildroot}%{_localstatedir}/lib/private +mkdir -p %{buildroot}%{_localstatedir}/log/private +mkdir -p %{buildroot}%{_localstatedir}/cache/private +mkdir -p %{buildroot}%{_localstatedir}/lib/private/systemd/journal-upload +ln -s ../private/systemd/journal-upload %{buildroot}%{_localstatedir}/lib/systemd/journal-upload +mkdir -p %{buildroot}%{_localstatedir}/log/journal +touch %{buildroot}%{_localstatedir}/lib/systemd/catalog/database +touch %{buildroot}%{_sysconfdir}/udev/hwdb.bin +touch %{buildroot}%{_localstatedir}/lib/systemd/random-seed +touch %{buildroot}%{_localstatedir}/lib/private/systemd/journal-upload/state + +# Install rc.local +mkdir -p %{buildroot}%{_sysconfdir}/rc.d/ +install -m 0644 %{SOURCE13} %{buildroot}%{_sysconfdir}/rc.d/rc.local +ln -s rc.d/rc.local %{buildroot}%{_sysconfdir}/rc.local + +# Install yum protection fragment +install -Dm0644 %{SOURCE4} %{buildroot}%{_sysconfdir}/dnf/protected.d/systemd.conf + +install -Dm0644 -t %{buildroot}/usr/lib/firewalld/services/ %{SOURCE7} %{SOURCE8} + +# Restore systemd-user pam config from before "removal of Fedora-specific bits" +install -Dm0644 -t %{buildroot}/etc/pam.d/ %{SOURCE12} + +# Install additional docs +# https://bugzilla.redhat.com/show_bug.cgi?id=1234951 +install -Dm0644 -t %{buildroot}%{_pkgdocdir}/ %{SOURCE9} + +# https://bugzilla.redhat.com/show_bug.cgi?id=1378974 +install -Dm0644 -t %{buildroot}%{system_unit_dir}/systemd-udev-trigger.service.d/ %{SOURCE10} + +install -Dm0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE11} + +install -D -t %{buildroot}/usr/lib/systemd/ %{SOURCE3} + +# No tmp-on-tmpfs by default in RHEL. bz#876122 bz#1578772 +rm -f %{buildroot}%{_prefix}/lib/systemd/system/local-fs.target.wants/tmp.mount + +# bz#1844465 +rm -f %{buildroot}/etc/systemd/system/dbus-org.freedesktop.resolve1.service + +%find_lang %{name} + +# Split files in build root into rpms. See split-files.py for the +# rules towards the end, anything which is an exception needs a line +# here. +python3 %{SOURCE2} %buildroot </dev/null || groupadd -r -g 11 cdrom &>/dev/null || : +getent group utmp &>/dev/null || groupadd -r -g 22 utmp &>/dev/null || : +getent group tape &>/dev/null || groupadd -r -g 33 tape &>/dev/null || : +getent group dialout &>/dev/null || groupadd -r -g 18 dialout &>/dev/null || : +getent group input &>/dev/null || groupadd -r input &>/dev/null || : +getent group kvm &>/dev/null || groupadd -r -g 36 kvm &>/dev/null || : +getent group render &>/dev/null || groupadd -r render &>/dev/null || : +getent group systemd-journal &>/dev/null || groupadd -r -g 190 systemd-journal 2>&1 || : + +getent group systemd-coredump &>/dev/null || groupadd -r systemd-coredump 2>&1 || : +getent passwd systemd-coredump &>/dev/null || useradd -r -l -g systemd-coredump -d / -s /sbin/nologin -c "systemd Core Dumper" systemd-coredump &>/dev/null || : + +getent group systemd-resolve &>/dev/null || groupadd -r -g 193 systemd-resolve 2>&1 || : +getent passwd systemd-resolve &>/dev/null || useradd -r -u 193 -l -g systemd-resolve -d / -s /sbin/nologin -c "systemd Resolver" systemd-resolve &>/dev/null || : + +%post +systemd-machine-id-setup &>/dev/null || : +systemctl daemon-reexec &>/dev/null || : +journalctl --update-catalog &>/dev/null || : +systemd-tmpfiles --create &>/dev/null || : + +# Make sure new journal files will be owned by the "systemd-journal" group +chgrp systemd-journal /run/log/journal/ /run/log/journal/`cat /etc/machine-id 2>/dev/null` /var/log/journal/ /var/log/journal/`cat /etc/machine-id 2>/dev/null` &>/dev/null || : +chmod g+s /run/log/journal/ /run/log/journal/`cat /etc/machine-id 2>/dev/null` /var/log/journal/ /var/log/journal/`cat /etc/machine-id 2>/dev/null` &>/dev/null || : + +# Apply ACL to the journal directory +setfacl -Rnm g:wheel:rx,d:g:wheel:rx,g:adm:rx,d:g:adm:rx /var/log/journal/ &>/dev/null || : + +# Stop-gap until rsyslog.rpm does this on its own. (This is supposed +# to fail when the link already exists) +ln -s /usr/lib/systemd/system/rsyslog.service /etc/systemd/system/syslog.service &>/dev/null || : + +# Remove spurious /etc/fstab entries from very old installations +# https://bugzilla.redhat.com/show_bug.cgi?id=1009023 +if [ -e /etc/fstab ]; then + grep -v -E -q '^(devpts|tmpfs|sysfs|proc)' /etc/fstab || \ + sed -i.rpm.bak -r '/^devpts\s+\/dev\/pts\s+devpts\s+defaults\s+/d; /^tmpfs\s+\/dev\/shm\s+tmpfs\s+defaults\s+/d; /^sysfs\s+\/sys\s+sysfs\s+defaults\s+/d; /^proc\s+\/proc\s+proc\s+defaults\s+/d' /etc/fstab || : +fi + +# Services we install by default, and which are controlled by presets. +if [ $1 -eq 1 ] ; then + systemctl preset --quiet \ + remote-fs.target \ + getty@.service \ + serial-getty@.service \ + console-getty.service \ + debug-shell.service \ + systemd-resolved.service \ + >/dev/null || : +fi + +# remove obsolete systemd-readahead file +rm -f /.readahead &>/dev/null || : + +%preun +if [ $1 -eq 0 ] ; then + systemctl disable --quiet \ + remote-fs.target \ + getty@.service \ + serial-getty@.service \ + console-getty.service \ + debug-shell.service \ + systemd-readahead-replay.service \ + systemd-readahead-collect.service \ + systemd-resolved.service \ + >/dev/null || : + + rm -f /etc/systemd/system/default.target &>/dev/null || : +fi + +%post libs +%{?ldconfig} + +function mod_nss() { + if [ -f "$1" ] ; then + # sed-fu to add myhostanme to hosts line + grep -E -q '^hosts:.* myhostname' "$1" || + sed -i.bak -e ' + /^hosts:/ !b + /\/ b + s/[[:blank:]]*$/ myhostname/ + ' "$1" &>/dev/null || : + + # Add nss-systemd to passwd and group + grep -E -q '^(passwd|group):.* systemd' "$1" || + sed -i.bak -r -e ' + s/^(passwd|group):(.*)/\1: \2 systemd/ + ' "$1" &>/dev/null || : + fi +} + +FILE="$(readlink /etc/nsswitch.conf || echo /etc/nsswitch.conf)" +mod_nss "$FILE" + +if [ "$FILE" = "/etc/authselect/user-nsswitch.conf" ] ; then + authselect apply-changes &> /dev/null +else + # also apply the same changes to nsswitch.conf to affect + # possible future authselect configuration + mod_nss "/etc/authselect/user-nsswitch.conf" +fi + +# check if nobody or nfsnobody is defined +export SYSTEMD_NSS_BYPASS_SYNTHETIC=1 +if getent passwd nfsnobody &>/dev/null; then + test -f /etc/systemd/dont-synthesize-nobody || { + echo 'Detected system with nfsnobody defined, creating /etc/systemd/dont-synthesize-nobody' + mkdir -p /etc/systemd || : + : >/etc/systemd/dont-synthesize-nobody || : + } +elif getent passwd nobody 2>/dev/null | grep -v 'nobody:[x*]:65534:65534:.*:/:/sbin/nologin' &>/dev/null; then + test -f /etc/systemd/dont-synthesize-nobody || { + echo 'Detected system with incompatible nobody defined, creating /etc/systemd/dont-synthesize-nobody' + mkdir -p /etc/systemd || : + : >/etc/systemd/dont-synthesize-nobody || : + } +fi + +%{?ldconfig:%postun libs -p %ldconfig} + +%global udev_services systemd-udev{d,-settle,-trigger}.service systemd-udevd-{control,kernel}.socket + +%post udev +# Move old stuff around in /var/lib +mv %{_localstatedir}/lib/random-seed %{_localstatedir}/lib/systemd/random-seed &>/dev/null +mv %{_localstatedir}/lib/backlight %{_localstatedir}/lib/systemd/backlight &>/dev/null + +udevadm hwdb --update &>/dev/null +%systemd_post %udev_services +/usr/lib/systemd/systemd-random-seed save 2>&1 + +# Replace obsolete keymaps +# https://bugzilla.redhat.com/show_bug.cgi?id=1151958 +grep -q -E '^KEYMAP="?fi-latin[19]"?' /etc/vconsole.conf 2>/dev/null && + sed -i.rpm.bak -r 's/^KEYMAP="?fi-latin[19]"?/KEYMAP="fi"/' /etc/vconsole.conf || : + +%postun udev +# Only restart systemd-udev, to run the upgraded dameon. +# Others are either oneshot services, or sockets, and restarting them causes issues (#1378974) +%systemd_postun_with_restart systemd-udevd.service + +%pre journal-remote +getent group systemd-journal-remote &>/dev/null || groupadd -r systemd-journal-remote 2>&1 || : +getent passwd systemd-journal-remote &>/dev/null || useradd -r -l -g systemd-journal-remote -d %{_localstatedir}/log/journal/remote -s /sbin/nologin -c "Journal Remote" systemd-journal-remote &>/dev/null || : + +%post journal-remote +%systemd_post systemd-journal-gatewayd.socket systemd-journal-gatewayd.service +%systemd_post systemd-journal-remote.socket systemd-journal-remote.service +%systemd_post systemd-journal-upload.service +%firewalld_reload + +%preun journal-remote +%systemd_preun systemd-journal-gatewayd.socket systemd-journal-gatewayd.service +%systemd_preun systemd-journal-remote.socket systemd-journal-remote.service +%systemd_preun systemd-journal-upload.service +if [ $1 -eq 1 ] ; then + if [ -f %{_localstatedir}/lib/systemd/journal-upload/state -a ! -L %{_localstatedir}/lib/systemd/journal-upload ] ; then + mkdir -p %{_localstatedir}/lib/private/systemd/journal-upload + mv %{_localstatedir}/lib/systemd/journal-upload/state %{_localstatedir}/lib/private/systemd/journal-upload/. + rmdir %{_localstatedir}/lib/systemd/journal-upload || : + fi +fi + +%postun journal-remote +%systemd_postun_with_restart systemd-journal-gatewayd.service +%systemd_postun_with_restart systemd-journal-remote.service +%systemd_postun_with_restart systemd-journal-upload.service +%firewalld_reload + +%global _docdir_fmt %{name} + +%files -f %{name}.lang -f .file-list-rest +%doc %{_pkgdocdir} +%exclude %{_pkgdocdir}/LICENSE.* +%license LICENSE.GPL2 LICENSE.LGPL2.1 +%ghost %dir %attr(0755,-,-) /etc/systemd/system/basic.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/bluetooth.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/default.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/getty.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/graphical.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/local-fs.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/machines.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/multi-user.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/printer.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/remote-fs.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/sockets.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/sysinit.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/system-update.target.wants +%ghost %dir %attr(0755,-,-) /etc/systemd/system/timers.target.wants +%ghost %dir %attr(0755,-,-) /var/lib/rpm-state/systemd + +%files libs -f .file-list-libs +%license LICENSE.LGPL2.1 + +%files pam -f .file-list-pam + +%files devel -f .file-list-devel + +%files udev -f .file-list-udev + +%files container -f .file-list-container + +%files journal-remote -f .file-list-remote + +%files tests -f .file-list-tests + +%changelog +* Thu Mar 11 2021 systemd maintenance team - 239-45 +- Revert "test: add test cases for empty string match" and "test: add test case for multi matches when use ||" (#1931947) +- test/sys-script.py: add missing DEVNAME entries to uevents (#1931947) +- sd-event: split out helper functions for reshuffling prioqs (#1819868) +- sd-event: split out enable and disable codepaths from sd_event_source_set_enabled() (#1819868) +- sd-event: mention that two debug logged events are ignored (#1819868) +- sd-event: split clock data allocation out of sd_event_add_time() (#1819868) +- sd-event: split out code to add/remove timer event sources to earliest/latest prioq (#1819868) +- sd-event: fix delays assert brain-o (#17790) (#1819868) +- sd-event: let's suffix last_run/last_log with "_usec" (#1819868) +- sd-event: refuse running default event loops in any other thread than the one they are default for (#1819868) +- sd-event: ref event loop while in sd_event_prepare() ot sd_event_run() (#1819868) +- sd-event: follow coding style with naming return parameter (#1819868) +- sd-event: remove earliest_index/latest_index into common part of event source objects (#1819868) +- sd-event: update state at the end in event_source_enable (#1819868) +- sd-event: increase n_enabled_child_sources just once (#1819868) +- sd-event: add ability to ratelimit event sources (#1819868) +- test: add ratelimiting test (#1819868) +- core: prevent excessive /proc/self/mountinfo parsing (#1819868) +- udev: run link_update() with increased retry count in second invocation (#1931947) +- pam-systemd: use secure_getenv() rather than getenv() (#1687514) + +* Thu Jan 28 2021 systemd maintenance team - 239-44 +- ci: PowerTools repo was renamed to powertools in RHEL 8.3 (#1871827) +- ci: use quay.io instead of Docker Hub to avoid rate limits (#1871827) +- ci: move jobs from Travis CI to GH Actions (#1871827) +- unit: make UNIT() cast function deal with NULL pointers (#1871827) +- use link to RHEL-8 docs (#1623116) +- cgroup: Also set blkio.bfq.weight (#1657810) +- units: make sure initrd-cleanup.service terminates before switching to rootfs (#1657810) +- core: reload SELinux label cache on daemon-reload (#1888912) +- selinux: introduce mac_selinux_create_file_prepare_at() (#1888912) +- selinux: add trigger for policy reload to refresh internal selabel cache (#1888912) +- udev/net_id: give RHEL-8.4 naming scheme a name (#1827462) +- basic/stat-util: make mtime check stricter and use entire timestamp (#1642728) +- udev: make algorithm that selects highest priority devlink less susceptible to race conditions (#1642728) +- test: create /dev/null in test-udev.pl (#1642728) +- test: missing "die" (#1642728) +- udev-test: remove a check for whether the test is run in a container (#1642728) +- udev-test: skip the test only if it can't setup its environment (#1642728) +- udev-test: fix test skip condition (#1642728) +- udev-test: fix missing directory test/run (#1642728) +- udev-test: check if permitted to create block device nodes (#1642728) +- test-udev: add a testcase of too long line (#1642728) +- test-udev: use proper semantics for too long line with continuation (#1642728) +- test-udev: add more tests for line continuations and comments (#1642728) +- test-udev: add more tests for line continuation (#1642728) +- test-udev: fix alignment and drop unnecessary white spaces (#1642728) +- test/udev-test.pl: cleanup if skipping test (#1642728) +- test: add test cases for empty string match (#1642728) +- test: add test case for multi matches when use "||" (#1642728) +- udev-test: do not rely on "mail" group being defined (#1642728) +- test/udev-test.pl: allow multiple devices per test (#1642728) +- test/udev-test.pl: create rules only once (#1642728) +- test/udev-test.pl: allow concurrent additions and removals (#1642728) +- test/udev-test.pl: use computed devnode name (#1642728) +- test/udev-test.pl: test correctness of symlink targets (#1642728) +- test/udev-test.pl: allow checking multiple symlinks (#1642728) +- test/udev-test.pl: fix wrong test descriptions (#1642728) +- test/udev-test.pl: last_rule is unsupported (#1642728) +- test/udev-test.pl: Make some tests a little harder (#1642728) +- test/udev-test.pl: remove bogus rules from magic subsys test (#1642728) +- test/udev-test.pl: merge "space and var with space" tests (#1642728) +- test/udev-test.pl: merge import parent tests into one (#1642728) +- test/udev-test.pl: count "good" results (#1642728) +- tests/udev-test.pl: add multiple device test (#1642728) +- test/udev-test.pl: add repeat count (#1642728) +- test/udev-test.pl: generator for large list of block devices (#1642728) +- test/udev-test.pl: suppress umount error message at startup (#1642728) +- test/udev_test.pl: add "expected good" count (#1642728) +- test/udev-test: gracefully exit when imports fail (#1642728) + +* Thu Nov 26 2020 systemd maintenance team - 239-43 +- man: mention System Administrator's Guide in systemctl manpage (#1623116) +- udev: introduce udev net_id "naming schemes" (#1827462) +- meson: make net.naming-scheme= default configurable (#1827462) +- man: describe naming schemes in a new man page (#1827462) +- udev/net_id: parse _SUN ACPI index as a signed integer (#1827462) +- udev/net_id: don't generate slot based names if multiple devices might claim the same slot (#1827462) +- fix typo in ProtectSystem= option (#1871139) +- remove references of non-existent man pages (#1876807) +- log: Prefer logging to CLI unless JOURNAL_STREAM is set (#1865840) +- locale-util: add new helper locale_is_installed() (#1755287) +- test: add test case for locale_is_installed() (#1755287) +- tree-wide: port various bits over to locale_is_installed() (#1755287) +- install: allow instantiated units to be enabled via presets (#1812972) +- install: small refactor to combine two function calls into one function (#1812972) +- test: fix a memleak (#1812972) +- docs: Add syntax for templated units to systemd.preset man page (#1812972) +- shared/install: fix preset operations for non-service instantiated units (#1812972) +- introduce setsockopt_int() helper (#1887181) +- socket-util: add generic socket_pass_pktinfo() helper (#1887181) +- core: add new PassPacketInfo= socket unit property (#1887181) +- resolved: tweak cmsg calculation (#1887181) + +* Tue Nov 03 2020 systemd maintenance team - 239-42 +- logind: don't print warning when user@.service template is masked (#1880270) +- build: use simple project version in pkgconfig files (#1862714) +- basic/virt: try the /proc/1/sched hack also for PID1 (#1868877) +- seccomp: rework how the S[UG]ID filter is installed (#1860374) +- vconsole-setup: downgrade log message when setting font fails on dummy console (#1889996) +- units: fix systemd.special man page reference in system-update-cleanup.service (#1871827) +- units: drop reference to sushell man page (#1871827) +- sd-bus: break the loop in bus_ensure_running() if the bus is not connecting (#1885553) +- core: add new API for enqueing a job with returning the transaction data (#846319) +- systemctl: replace switch statement by table of structures (#846319) +- systemctl: reindent table (#846319) +- systemctl: Only wait when there's something to wait for. (#846319) +- systemctl: clean up start_unit_one() error handling (#846319) +- systemctl: split out extra args generation into helper function of its own (#846319) +- systemctl: add new --show-transaction switch (#846319) +- test: add some basic testing that "systemctl start -T" does something (#846319) +- man: document the new systemctl --show-transaction option (#846319) +- socket: New option 'FlushPending' (boolean) to flush socket before entering listening state (#1870638) +- core: remove support for API bus "started outside our own logic" (#1764282) +- mount-setup: fix segfault in mount_cgroup_controllers when using gcc9 compiler (#1868877) +- dbus-execute: make transfer of CPUAffinity endian safe (#12711) (#1740657) +- core: add support for setting CPUAffinity= to special "numa" value (#1740657) +- basic/user-util: always use base 10 for user/group numbers (#1848373) +- parse-util: sometimes it is useful to check if a string is a valid integer, but not actually parse it (#1848373) +- basic/parse-util: add safe_atoux64() (#1848373) +- parse-util: allow tweaking how to parse integers (#1848373) +- parse-util: allow '-0' as alternative to '0' and '+0' (#1848373) +- parse-util: make return parameter optional in safe_atou16_full() (#1848373) +- parse-util: rewrite parse_mode() on top of safe_atou_full() (#1848373) +- user-util: be stricter in parse_uid() (#1848373) +- strv: add new macro STARTSWITH_SET() (#1848373) +- parse-util: also parse integers prefixed with 0b and 0o (#1848373) +- tests: beef up integer parsing tests (#1848373) +- shared/user-util: add compat forms of user name checking functions (#1848373) +- shared/user-util: emit a warning on names with dots (#1848373) +- user-util: Allow names starting with a digit (#1848373) +- shared/user-util: allow usernames with dots in specific fields (#1848373) +- user-util: switch order of checks in valid_user_group_name_or_id_full() (#1848373) +- user-util: rework how we validate user names (#1848373) + +* Wed Oct 07 2020 systemd maintenance team - 239-41 +- cgroup: freezer action must be NOP when cgroup v2 freezer is not available (#1868831) + +* Fri Aug 28 2020 systemd maintenance team - 239-40 +- units: add generic boot-complete.target (#1872243) +- man: document new "boot-complete.target" unit (#1872243) +- core: make sure to restore the control command id, too (#1829867) + +* Thu Aug 06 2020 systemd maintenance team - 239-39 +- device: make sure we emit PropertiesChanged signal once we set sysfs (#1793533) +- device: don't emit PropetiesChanged needlessly (#1793533) + +* Tue Aug 04 2020 systemd maintenance team - 239-38 +- spec: fix rpm verification (#1702300) + +* Wed Jul 08 2020 systemd maintenance team - 239-37 +- spec: don't package /etc/systemd/system/dbus-org.freedesktop.resolve1.service (#1844465) + +* Fri Jun 26 2020 systemd maintenance team - 239-36 +- core: don't consider SERVICE_SKIP_CONDITION for abnormal or failure restarts (#1737283) +- selinux: do preprocessor check only in selinux-access.c (#1830861) +- basic/cgroup-util: introduce cg_get_keyed_attribute_full() (#1830861) +- shared: add generic logic for waiting for a unit to enter some state (#1830861) +- shared: fix assert call (#1830861) +- shared: Don't try calling NULL callback in bus_wait_for_units_clear (#1830861) +- shared: add NULL callback check in one more place (#1830861) +- core: introduce support for cgroup freezer (#1830861) +- core/cgroup: fix return value of unit_cgorup_freezer_action() (#1830861) +- core: fix the return value in order to make sure we don't dipatch method return too early (#1830861) +- test: add test for cgroup v2 freezer support (#1830861) +- fix mis-merge (#1848421) +- tests: sleep a bit and give kernel time to perform the action after manual freeze/thaw (#1848421) + +* Fri Jun 26 2020 systemd maintenance team - 239-35 +- spec: fix rpm verification (#1702300) + +* Thu Jun 18 2020 systemd maintenance team - 239-34 +- spec: fix rpm verification (#1702300) + +* Tue Jun 09 2020 systemd maintenance team - 239-33 +- tmpfiles: fix crash with NULL in arg_root and other fixes and tests (#1836024) +- sulogin-shell: Use force if SYSTEMD_SULOGIN_FORCE set (#1625929) +- resolvconf: fixes for the compatibility interface (#1835594) +- mount: don't add Requires for tmp.mount (#1748840) +- core: coldplug possible nop_job (#1829798) +- core: add IODeviceLatencyTargetSec (#1831519) +- time-util: Introduce parse_sec_def_infinity (#1770379) +- cgroup: use structured initialization (#1770379) +- core: add CPUQuotaPeriodSec= (#1770379) +- core: downgrade CPUQuotaPeriodSec= clamping logs to debug (#1770379) +- sd-bus: avoid magic number in SASL length calculation (#1838081) +- sd-bus: fix SASL reply to empty AUTH (#1838081) +- sd-bus: skip sending formatted UIDs via SASL (#1838081) +- core: add MemoryMin (#1763435) +- core: introduce cgroup_add_device_allow() (#1763435) +- test: remove support for suffix in get_testdata_dir() (#1763435) +- cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow (#1763435) +- cgroup: Create UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP (#1763435) +- unit: Add DefaultMemoryMin (#1763435) +- cgroup: Polish hierarchically aware protection docs a bit (#1763435) +- cgroup: Readd some plumbing for DefaultMemoryMin (#1763435) +- cgroup: Support 0-value for memory protection directives (#1763435) +- cgroup: Test that it's possible to set memory protection to 0 again (#1763435) +- cgroup: Check ancestor memory min for unified memory config (#1763435) +- cgroup: Respect DefaultMemoryMin when setting memory.min (#1763435) +- cgroup: Mark memory protections as explicitly set in transient units (#1763435) +- meson: allow setting the version string during configuration (#1804252) + +* Thu Jun 04 2020 systemd maintenance team - 239-32 +- pid1: fix DefaultTasksMax initialization (#1809037) +- cgroup: make sure that cpuset is supported on cgroup v2 and disabled with v1 (#1808940) +- test: introduce TEST-36-NUMAPOLICY (#1808940) +- test: replace `tail -f` with journal cursor which should be... (#1808940) +- test: support MPOL_LOCAL matching in unpatched strace versions (#1808940) +- test: make sure the strace process is indeed dead (#1808940) +- test: skip the test on systems without NUMA support (#1808940) +- test: give strace some time to initialize (#1808940) +- test: add a simple sanity check for systems without NUMA support (#1808940) +- test: drop the missed || exit 1 expression (#1808940) +- test: replace cursor file with a plain cursor (#1808940) +- cryptsetup: Treat key file errors as a failed password attempt (#1763155) +- swap: finish the secondary swap units' jobs if deactivation of the primary swap unit fails (#1749622) +- resolved: Recover missing PrivateTmp=yes and ProtectSystem=strict (#1810869) +- bus_open leak sd_event_source when udevadm trigger。 (#1798504) +- core: rework StopWhenUnneeded= logic (#1798046) +- pid1: fix the names of AllowedCPUs= and AllowedMemoryNodes= (#1818054) +- core: fix re-realization of cgroup siblings (#1818054) +- basic: use comma as separator in cpuset cgroup cpu ranges (#1818054) +- core: transition to FINAL_SIGTERM state after ExecStopPost= (#1766479) +- sd-journal: close journal files that were deleted by journald before we've setup inotify watch (#1796128) +- sd-journal: remove the dead code and actually fix #14695 (#1796128) +- udev: downgrade message when we fail to set inotify watch up (#1808051) +- logind: check PolicyKit before allowing VT switch (#1797679) +- test: do not use global variable to pass error (#1823767) +- test: install libraries required by tests (#1823767) +- test: introduce install_zoneinfo() (#1823767) +- test: replace duplicated Makefile by symbolic link (#1823767) +- test: add paths of keymaps in install_keymaps() (#1823767) +- test: make install_keymaps() optionally install more keymaps (#1823767) +- test-fs-util: skip some tests when running in unprivileged container (#1823767) +- test-process-util: skip several verifications when running in unprivileged container (#1823767) +- test-execute: also check python3 is installed or not (#1823767) +- test-execute: skip several tests when running in container (#1823767) +- test: introduce test_is_running_from_builddir() (#1823767) +- test: make test-catalog relocatable (#1823767) +- test: parallelize tasks in TEST-24-UNIT-TESTS (#1823767) +- test: try to determine QEMU_SMP dynamically (#1823767) +- test: store coredumps in journal (#1823767) +- pid1: add new kernel cmdline arg systemd.cpu_affinity= (#1812894) +- udev-rules: make tape-changers also apprear in /dev/tape/by-path/ (#1820112) +- man: be clearer that .timer time expressions need to be reset to override them (#1816908) +- Add support for opening files for appending (#1809175) +- nspawn: move payload to sub-cgroup first, then sync cgroup trees (#1837094) +- core: move unit_status_emit_starting_stopping_reloading() and related calls to job.c (#1737283) +- job: when a job was skipped due to a failed condition, log about it (#1737283) +- core: split out all logic that updates a Job on a unit's unit_notify() invocation (#1737283) +- core: make log messages about units entering a 'failed' state recognizable (#1737283) +- core: log a recognizable message when a unit succeeds, too (#1737283) +- tests: always use the right vtable wrapper calls (#1737283) +- test-execute: allow filtering test cases by pattern (#1737283) +- test-execute: provide custom failure message (#1737283) +- core: ExecCondition= for services (#1737283) +- Drop support for lz4 < 1.3.0 (#1843871) +- test-compress: add test for short decompress_startswith calls (#1843871) +- journal: adapt for new improved LZ4_decompress_safe_partial() (#1843871) +- fuzz-compress: add fuzzer for compression and decompression (#1843871) +- seccomp: fix __NR__sysctl usage (#1843871) + +* Fri Feb 21 2020 systemd maintenance team - 239-27 +- cgroup: introduce support for cgroup v2 CPUSET controller (#1724617) + +* Wed Feb 19 2020 systemd maintenance team - 239-26 +- seccomp: introduce seccomp_restrict_suid_sgid() for blocking chmod() for suid/sgid files (#1687512) +- test: add test case for restrict_suid_sgid() (#1687512) +- core: expose SUID/SGID restriction as new unit setting RestrictSUIDSGID= (#1687512) +- analyze: check for RestrictSUIDSGID= in "systemd-analyze security" (#1687512) +- man: document the new RestrictSUIDSGID= setting (#1687512) +- units: turn on RestrictSUIDSGID= in most of our long-running daemons (#1687512) +- core: imply NNP and SUID/SGID restriction for DynamicUser=yes service (#1687512) + +* Mon Feb 17 2020 systemd maintenance team - 239-25 +- sd-bus: use "queue" message references for managing r/w message queues in connection objects (CVE-2020-1712) +- pid1: make sure to restore correct default values for some rlimits (#1789930) +- main: introduce a define HIGH_RLIMIT_MEMLOCK similar to HIGH_RLIMIT_NOFILE (#1789930) + +* Thu Feb 13 2020 systemd maintenance team - 239-24 +- rules: reintroduce 60-alias-kmsg.rules (#1739353) +- sd-bus: make rqueue/wqueue sizes of type size_t (CVE-2020-1712) +- sd-bus: reorder bus ref and bus message ref handling (CVE-2020-1712) +- sd-bus: make sure dispatch_rqueue() initializes return parameter on all types of success (CVE-2020-1712) +- sd-bus: drop two inappropriate empty lines (CVE-2020-1712) +- sd-bus: initialize mutex after we allocated the wqueue (CVE-2020-1712) +- sd-bus: always go through sd_bus_unref() to free messages (CVE-2020-1712) +- bus-message: introduce two kinds of references to bus messages (CVE-2020-1712) +- sd-bus: introduce API for re-enqueuing incoming messages (CVE-2020-1712) +- sd-event: add sd_event_source_disable_unref() helper (CVE-2020-1712) +- polkit: when authorizing via PK let's re-resolve callback/userdata instead of caching it (CVE-2020-1712) +- sysctl: let's by default increase the numeric PID range from 2^16 to 2^22 (#1744214) +- journal: do not trigger assertion when journal_file_close() get NULL (#1788085) +- journal: use cleanup attribute at one more place (#1788085) + +* Mon Jan 13 2020 systemd maintenance team - 239-23 +- catalog: fix name of variable (#1677768) +- cryptsetup: add keyfile-timeout to allow a keydev timeout and allow to fallback to a password if it fails. (#1763155) +- cryptsetup: add documentation for keyfile-timeout (#1763155) +- cryptsetup: use unabbrieviated variable names (#1763155) +- cryptsetup: don't assert on variable which is optional (#1763155) +- cryptsetup-generator: guess whether the keyfile argument is two items or one (#1763155) +- crypt-util: Translate libcryptsetup log level instead of using log_debug() (#1776408) +- cryptsetup: add some commenting about EAGAIN generation (#1776408) +- cryptsetup: downgrade a log message we ignore (#1776408) +- cryptsetup: rework how we log about activation failures (#1776408) + +* Tue Dec 17 2019 systemd maintenance team - 239-22 +- spec: don't ship /var/log/README +- spec: provide systemd-rpm-macros + +* Mon Dec 09 2019 systemd maintenance team - 239-21 +- test-cpu-set-util: fix comparison for allocation size (#1734787) +- test-cpu-set-util: fix allocation size check on i386 (#1734787) + +* Mon Dec 09 2019 systemd maintenance team - 239-20 +- journal: rely on _cleanup_free_ to free a temporary string used in client_context_read_cgroup (#1764560) +- basic/user-util: allow dots in user names (#1717603) +- sd-bus: bump message queue size again (#1770189) +- tests: put fuzz_journald_processing_function in a .c file (#1764560) +- tests: add a fuzzer for dev_kmsg_record (#1764560) +- basic: remove an assertion from cunescape_one (#1764560) +- journal: fix an off-by-one error in dev_kmsg_record (#1764560) +- tests: add a reproducer for a memory leak fixed in 30eddcd51b8a472e05d3b8d1 in August (#1764560) +- tests: add a reproducer for a heap-buffer-overflow fixed in 937b1171378bc1000a (#1764560) +- test: initialize syslog_fd in fuzz-journald-kmsg too (#1764560) +- tests: add a fuzzer for process_audit_string (#1764560) +- journald: check whether sscanf has changed the value corresponding to %n (#1764560) +- tests: introduce dummy_server_init and use it in all journald fuzzers (#1764560) +- tests: add a fuzzer for journald streams (#1764560) +- tests: add a fuzzer for server_process_native_file (#1764560) +- fuzz-journal-stream: avoid assertion failure on samples which don't fit in pipe (#1764560) +- journald: take leading spaces into account in syslog_parse_identifier (#1764560) +- Add a warning about the difference in permissions between existing directories and unit settings. (#1778384) +- execute: remove one redundant comparison check (#1778384) +- core: change ownership/mode of the execution directories also for static users (#1778384) +- core/dbus-execute: remove unnecessary initialization (#1734787) +- shared/cpu-set-util: move the part to print cpu-set into a separate function (#1734787) +- shared/cpu-set-util: remove now-unused CPU_SIZE_TO_NUM() (#1734787) +- Rework cpu affinity parsing (#1734787) +- Move cpus_in_affinity_mask() to cpu-set-util.[ch] (#1734787) +- test-cpu-set-util: add simple test for cpus_in_affinity_mask() (#1734787) +- test-cpu-set-util: add a smoke test for test_parse_cpu_set_extend() (#1734787) +- pid1: parse CPUAffinity= in incremental fashion (#1734787) +- pid1: don't reset setting from /proc/cmdline upon restart (#1734787) +- pid1: when reloading configuration, forget old settings (#1734787) +- test-execute: use CPUSet too (#1734787) +- shared/cpu-set-util: drop now-unused cleanup function (#1734787) +- shared/cpu-set-util: make transfer of cpu_set_t over bus endian safe (#1734787) +- test-cpu-set-util: add test for dbus conversions (#1734787) +- shared/cpu-set-util: introduce cpu_set_to_range() (#1734787) +- systemctl: present CPUAffinity mask as a list of CPU index ranges (#1734787) +- shared/cpu-set-util: only force range printing one time (#1734787) +- execute: dump CPUAffinity as a range string instead of a list of CPUs (#1734787) +- cpu-set-util: use %d-%d format in cpu_set_to_range_string() only for actual ranges (#1734787) +- core: introduce NUMAPolicy and NUMAMask options (#1734787) +- core: disable CPUAccounting by default (#1734787) +- set kptr_restrict=1 (#1689346) +- cryptsetup: reduce the chance that we will be OOM killed (#1696602) +- core, job: fix breakage of ordering dependencies by systemctl reload command (#1766417) +- debug-generator: enable custom systemd.debug_shell tty (#1723722) + +* Thu Oct 24 2019 Lukas Nykryn - 239-19 +- core: never propagate reload failure to service result (#1735787) +- man: document systemd-analyze security (#1750343) +- man: reorder and add examples to systemd-analyze(1) (#1750343) +- travis: move to CentOS 8 docker images (#1761519) +- travis: drop SCL remains (#1761519) +- syslog: fix segfault in syslog_parse_priority() (#1761519) +- sd-bus: make strict asan shut up (#1761519) +- travis: don't run slow tests under ASan/UBSan (#1761519) +- kernel-install: do not require non-empty kernel cmdline (#1701454) +- ask-password: prevent buffer overrow when reading from keyring (#1752050) +- core: try to reopen /dev/kmsg again right after mounting /dev (#1749212) +- buildsys: don't garbage collect sections while linking (#1748258) +- udev: introduce CONST key name (#1762679) +- Call getgroups() to know size of supplementary groups array to allocate (#1743230256 KB +#1743235) +- Consider smb3 as remote filesystem (#1757257) +- process-util: introduce pid_is_my_child() helper (#1744972) +- core: reduce the number of stalled PIDs from the watched processes list when possible (#1744972) +- core: only watch processes when it's really necessary (#1744972) +- core: implement per unit journal rate limiting (#1719577) +- path: stop watching path specs once we triggered the target unit (#1763161) +- journald: fixed assertion failure when system journal rotation fails (#9893) (#1763619) +- test: use PBKDF2 instead of Argon2 in cryptsetup... (#1761519) +- test: mask several unnecessary services (#1761519) +- test: bump the second partition's size to 50M (#1761519) +- shared/sleep-config: exclude zram devices from hibernation candidates (#1763617) +- selinux: don't log SELINUX_INFO and SELINUX_WARNING messages to audit (#1763612) +- sd-device: introduce log_device_*() macros (#1753369) +- udev: Add id program and rule for FIDO security tokens (#1753369) +- shared/but-util: drop trusted annotation from bus_open_system_watch_bind_with_description() (#1746857) +- sd-bus: adjust indentation of comments (#1746857) +- resolved: do not run loop twice (#1746857) +- resolved: allow access to Set*Link and Revert methods through polkit (#1746857) +- resolved: query polkit only after parsing the data (#1746857) + +* Fri Aug 30 2019 Lukas Nykryn - 239-18 +- shared/but-util: drop trusted annotation from bus_open_system_watch_bind_with_description() (#1746857) +- sd-bus: adjust indentation of comments (#1746857) +- resolved: do not run loop twice (#1746857) +- resolved: allow access to Set*Link and Revert methods through polkit (#1746857) +- resolved: query polkit only after parsing the data (#1746857) + +* Wed Aug 07 2019 Lukas Nykryn - 239-17 +- mount: simplify /proc/self/mountinfo handler (#1696178) +- mount: rescan /proc/self/mountinfo before processing waitid() results (#1696178) +- swap: scan /proc/swaps before processing waitid() results (#1696178) +- analyze-security: fix potential division by zero (#1734400) + +* Fri Jul 26 2019 Lukas Nykryn - 239-16 +- sd-bus: deal with cookie overruns (#1694999) +- journal-remote: do not request Content-Length if Transfer-Encoding is chunked (#1708849) +- journal: do not remove multiple spaces after identifier in syslog message (#1691817) +- cryptsetup: Do not fallback to PLAIN mapping if LUKS data device set fails. (#1719153) +- cryptsetup: call crypt_load() for LUKS only once (#1719153) +- cryptsetup: Add LUKS2 token support. (#1719153) +- udev/scsi_id: fix incorrect page length when get device identification VPD page (#1713227) +- Change job mode of manager triggered restarts to JOB_REPLACE (#11456 +#1712524) +- bash-completion: analyze: support 'security' (#1733395) +- man: note that journal does not validate syslog fields (#1707175) +- rules: skip memory hotplug on ppc64 (#1713159) + +* Thu May 23 2019 Lukas Nykryn - 239-15 +- tree-wide: shorten error logging a bit (#1697893) +- nspawn: simplify machine terminate bus call (#1697893) +- nspawn: merge two variable declaration lines (#1697893) +- nspawn: rework how we allocate/kill scopes (#1697893) +- unit: enqueue cgroup empty check event if the last ref on a unit is dropped (#1697893) +- Revert "journal: remove journal audit socket" (#1699287) +- journal: don't enable systemd-journald-audit.socket by default (#1699287) +- logs-show: use grey color for de-emphasizing journal log output (#1695601) +- units: add [Install] section to tmp.mount (#1667065) +- nss: do not modify errno when NSS_STATUS_NOTFOUND or NSS_STATUS_SUCCESS (#1691691) +- util.h: add new UNPROTECT_ERRNO macro (#1691691) +- nss: unportect errno before writing to NSS' *errnop (#1691691) +- seccomp: reduce logging about failure to add syscall to seccomp (#1658691) +- format-table: when duplicating a cell, also copy the color (#1689832) +- format-table: optionally make specific cells clickable links (#1689832) +- format-table: before outputting a color, check if colors are available (#1689832) +- format-table: add option to store/format percent and uint64_t values in cells (#1689832) +- format-table: optionally allow reversing the sort order for a column (#1689832) +- format-table: add table_update() to update existing entries (#1689832) +- format-table: add an API for getting the cell at a specific row/column (#1689832) +- format-table: always underline header line (#1689832) +- format-table: add calls to query the data in a specific cell (#1689832) +- format-table: make sure we never call memcmp() with NULL parameters (#1689832) +- format-table: use right field for display (#1689832) +- format-table: add option to uppercase cells on display (#1689832) +- format-table: never try to reuse cells that have color/url/uppercase set (#1689832) +- locale-util: add logic to output smiley emojis at various happiness levels (#1689832) +- analyze: add new security verb (#1689832) +- tests: add a rudimentary fuzzer for server_process_syslog_message (#9979) (#1696224) +- journald: make it clear that dev_kmsg_record modifies the string passed to it (#1696224) +- journald: free the allocated memory before returning from dev_kmsg_record (#1696224) +- tests: rework the code fuzzing journald (#1696224) +- journald: make server_process_native_message compatible with fuzz_journald_processing_function (#1696224) +- tests: add a fuzzer for server_process_native_message (#1696224) +- tests: add a fuzzer for sd-ndisc (#1696224) +- ndisc: fix two infinite loops (#1696224) +- tests: add reproducers for several issues uncovered with fuzz-journald-syslog (#1696224) +- tests: add a reproducer for an infinite loop in ndisc_handle_datagram (#1696224) +- tests: add a reproducer for another infinite loop in ndisc_handle_datagram (#1696224) +- fuzz: rename "fuzz-corpus" directory to just "fuzz" (#1696224) +- test: add testcase for issue 10007 by oss-fuzz (#1696224) +- fuzz: unify the "fuzz-regressions" directory with the main corpus (#1696224) +- test-bus-marshal: use cescaping instead of hexmem (#1696224) +- meson: add -Dlog-trace to set LOG_TRACE (#1696224) +- meson: allow building resolved and machined without nss modules (#1696224) +- meson: drop duplicated condition (#1696224) +- meson: use .source_root() in more places (#1696224) +- meson: treat all fuzz cases as unit tests (#1696224) +- fuzz-bus-message: add fuzzer for message parsing (#1696224) +- bus-message: use structured initialization to avoid use of unitialized memory (#1696224) +- bus-message: avoid an infinite loop on empty structures (#1696224) +- bus-message: let's always use -EBADMSG when the message is bad (#1696224) +- bus-message: rename function for clarity (#1696224) +- bus-message: use define (#1696224) +- bus: do not print (null) if the message has unknown type (#1696224) +- bus-message: fix calculation of offsets table (#1696224) +- bus-message: remove duplicate assignment (#1696224) +- bus-message: fix calculation of offsets table for arrays (#1696224) +- bus-message: drop asserts in functions which are wrappers for varargs version (#1696224) +- bus-message: output debug information about offset troubles (#1696224) +- bus-message: fix skipping of array fields in !gvariant messages (#1696224) +- bus-message: also properly copy struct signature when skipping (#1696224) +- fuzz-bus-message: add two test cases that pass now (#1696224) +- bus-message: return -EBADMSG not -EINVAL on invalid !gvariant messages (#1696224) +- bus-message: avoid wrap-around when using length read from message (#1696224) +- util: do not use stack frame for parsing arbitrary inputs (#1696224) +- travis: enable ASan and UBSan on RHEL8 (#1683319) +- tests: keep SYS_PTRACE when running under ASan (#1683319) +- tree-wide: various ubsan zero size memory fixes (#1683319) +- util: introduce memcmp_safe() (#1683319) +- test-socket-util: avoid "memleak" reported by valgrind (#1683319) +- sd-journal: escape binary data in match_make_string() (#1683319) +- capability: introduce CAP_TO_MASK_CORRECTED() macro replacing CAP_TO_MASK() (#1683319) +- sd-bus: use size_t when dealing with memory offsets (#1683319) +- sd-bus: call cap_last_cap() only once in has_cap() (#1683319) +- mount-point: honour AT_SYMLINK_FOLLOW correctly (#1683319) +- travis: switch from trusty to xenial (#1683319) +- test-socket-util: Add tests for receive_fd_iov() and friends. (#1683319) +- socket-util: Introduce send_one_fd_iov() and receive_one_fd_iov() (#1683319) +- core: swap order of "n_storage_fds" and "n_socket_fds" parameters (#1683334) +- execute: use our usual syntax for defining bit masks (#1683334) +- core: introduce new Type=exec service type (#1683334) +- man: document the new Type=exec type (#1683334) +- sd-bus: allow connecting to the pseudo-container ".host" (#1683334) +- sd-login: let's also make sd-login understand ".host" (#1683334) +- test: add test for Type=exec (#1683334) +- journal-gateway: explicitly declare local variables (#1705971) +- tools: drop unused variable (#1705971) +- journal-gateway: use localStorage["cursor"] only when it has valid value (#1705971) + +* Tue Apr 30 2019 Lukas Nykryn - 239-14 +- rules: implement new memory hotplug policy (#1670728) +- rules: add the rule that adds elevator= kernel command line parameter (#1670126) +- bus-socket: Fix line_begins() to accept word matching full string (#1692991) +- Refuse dbus message paths longer than BUS_PATH_SIZE_MAX limit. (#1678641) +- Allocate temporary strings to hold dbus paths on the heap (#1678641) +- sd-bus: if we receive an invalid dbus message, ignore and proceeed (#1678641) +- Revert "core: one step back again, for nspawn we actually can't wait for cgroups running empty since systemd will get exactly zero notifications about it" (#1703485) + +* Tue Feb 26 2019 Lukas Nykryn - 239-13 +- rules: add the rule that adds elevator= kernel command line parameter (#1670126) + +* Fri Feb 15 2019 Lukas Nykryn - 239-12 +- core: when deserializing state always use read_line(…, LONG_LINE_MAX, …) (CVE-2018-15686) +- coredump: remove duplicate MESSAGE= prefix from message (#1664976) +- journald: remove unnecessary {} (#1664976) +- journald: do not store the iovec entry for process commandline on stack (#1664976) +- basic/process-util: limit command line lengths to _SC_ARG_MAX (#1664976) +- coredump: fix message when we fail to save a journald coredump (#1664976) +- procfs-util: expose functionality to query total memory (#1664976) +- basic/prioq: add prioq_peek_item() (#1664976) +- journal: limit the number of entries in the cache based on available memory (#1664976) +- journald: periodically drop cache for all dead PIDs (#1664976) +- process-util: don't use overly large buffer to store process command line (#1664976) +- Revert "sysctl.d: switch net.ipv4.conf.all.rp_filter from 1 to 2" (#1653824) +- journal: fix syslog_parse_identifier() (#1664978) +- journald: set a limit on the number of fields (1k) (#1664977) +- journald: when processing a native message, bail more quickly on overbig messages (#1664977) +- journald: lower the maximum entry size limit to ½ for non-sealed fds (#1664977) +- µhttpd: use a cleanup function to call MHD_destroy_response (#1664977) +- journal-remote: verify entry length from header (#1664977) +- journal-remote: set a limit on the number of fields in a message (#1664977) +- journald: correctly attribute log messages also with cgroupsv1 (#1658115) +- rules: add elevator= kernel command line parameter (#1670126) + +* Mon Jan 14 2019 Lukas Nykryn - 239-11 +- unit: don't add Requires for tmp.mount (#1619292) +- remove bootchart dependency (#1660119) + +* Wed Dec 12 2018 Lukas Nykryn - 239-10 +- cryptsetup-generator: introduce basic keydev support (#1656869) +- cryptsetup: don't use %m if there's no error to show (#1656869) +- cryptsetup-generator: don't return error if target directory already exists (#1656869) +- cryptsetup-generator: allow whitespace characters in keydev specification (#1656869) +- rules: watch metadata changes on DASD devices (#1638676) +- sysctl.d: switch net.ipv4.conf.all.rp_filter from 1 to 2 (#1653824) + +* Thu Dec 06 2018 Lukas Nykryn - 239-9 +- dissect-image: use right comparison function (#1602706) +- login: avoid leak of name returned by uid_to_name() (#1602706) +- firewall-util: add an assert that we're not overwriting a buffer (#1602706) +- journal-file: avoid calling ftruncate with invalid fd (#1602706) +- dhcp6: make sure we have enough space for the DHCP6 option header (#1643363) +- core: rename queued_message → pending_reload_message (#1647359) +- core: when we can't send the pending reload message, say we ignore it in the warning we log (#1647359) +- core: make sure we don't throttle change signal generator when a reload is pending (#1647359) +- proc-cmdline: introduce PROC_CMDLINE_RD_STRICT (#1643429) +- debug-generator: introduce rd.* version of all options (#1643429) +- chown-recursive: let's rework the recursive logic to use O_PATH (#1643368) +- chown-recursive: also drop ACLs when recursively chown()ing (#1643368) +- chown-recursive: TAKE_FD() is your friend (#1643368) +- test: add test case for recursive chown()ing (#1643368) +- Revert "sysctl.d: request ECN on both in and outgoing connections" (#1619790) +- detect-virt: do not try to read all of /proc/cpuinfo (#1631532) +- sd-bus: unify three code-paths which free struct bus_container (#1635435) +- sd-bus: properly initialize containers (#1635435) + +* Tue Oct 16 2018 Lukas Nykryn - 239-8 +- revert sd-bus: unify three code-paths which free struct bus_container (#1635435) + +* Fri Oct 12 2018 Michal Sekletár - 239-7 +- change default cgroup hierarchy to "legacy" (#1638650) +- we never added mymachines module to passwd: or group: in RHEL8, hence don't try to remove it (#1638450) +- bump minimal size of random pool to 1024 bytes (#1619268) +- install RHEL-7 compatible rc.local (#1625209) +- backport support for sector-size crypttab option (#1572563) +- units: don't enable per-service IP firewall by default (#1630219) +- sd-bus: unify three code-paths which free struct bus_container (#1635435) +- bus-message: do not crash on message with a string of zero length (#1635439) +- bus-message: stack based buffer overflow in free_and_strdup (#1635428) +- journal: change support URL shown in the catalog entries (#1550548) + +* Mon Sep 10 2018 Michal Sekletár - 239-6 +- move /etc/yum/protected.d/systemd.conf to /etc/dnf/ (#1626973) + +* Fri Sep 07 2018 Josh Boyer - 239-5 +- Fix file conflict between yum and systemd (#1626682) + +* Tue Aug 14 2018 Michal Sekletár - 239-4 +- drop the patch for delayed loading of config in net_setup_link and set NAME in prefixdevname udev rules (#1614681) +- bus: move BUS_DONT_DESTROY calls after asserts (#1610397) + +* Fri Aug 10 2018 Michal Sekletár - 239-3 +- net_setup_link: delay loading configuration, just before we apply it (#1614681) + +* Thu Aug 09 2018 Michal Sekletár - 239-2 +- 20-grubby.install: populate symvers.gz file (#1609698) +- net_setup_link: allow renaming interfaces that were renamed already +- units: drop DynamicUser=yes from systemd-resolved.service +- journal: remove journal audit socket + +* Wed Aug 01 2018 Michal Sekletár - 239-1 +- rebase to systemd-239 +- Override systemd-user PAM config in install and not prep (patch by Filipe Brandenburger ) +- use %%autosetup -S git_am to apply patches +- revert upstream default for RemoveIPC (#1523233) +- bump DefaultTasksMax to 80% of kernel default (#1523236) +- avoid /tmp being mounted as tmpfs without the user's will (#1578772) +- bump maximum number of processes in user slice to 80% of pid.max (#1523236) +- forwardport downstream-only udev rules from RHEL-7 (#1523227) +- don't ship systemd-networkd +- don't ship systemd-timesyncd +- add back support for WAIT_FOR to udev rules (#1523213) + +* Wed May 16 2018 Jan Synáček - 238-8 +- do not mount /tmp as tmpfs (#1578772) + +* Tue May 15 2018 Jan Synáček - 238-7 +- fix compilation (#1578318) + +* Fri Apr 27 2018 Michal Sekletar - 238-6 +- forwardport downstream-only udev rules from RHEL-7 (#1523227) +- set RemoveIPC=no by default (#1523233) + +* Thu Apr 12 2018 Michal Sekletar - 238-5 +- also drop qrencode-devel from BuildRequires as it is no longer needed (#1566158) + +* Wed Apr 11 2018 Michal Sekletar - 238-4 +- disable support for qrencode (#1566158) +- bump default journal rate limit to 10000 messages per 30s (#1563729) +- fix unit reloads (#1560549) +- don't create /var/log/journal during package installation (#1523188) + +* Fri Mar 09 2018 Troy Dawson - 238-3.1 +- Rebuild with cryptsetup-2 + +* Wed Mar 7 2018 Zbigniew Jędrzejewski-Szmek - 238-3 +- Revert the patches for GRUB BootLoaderSpec support +- Add patch for /etc/machine-id creation (#1552843) + +* Tue Mar 6 2018 Yu Watanabe - 238-2 +- Fix transfiletrigger script (#1551793) + +* Mon Mar 5 2018 Zbigniew Jędrzejewski-Szmek - 238-1 +- Update to latest version +- This fixes a hard-to-trigger potential vulnerability (CVE-2018-6954) +- New transfiletriggers are installed for udev hwdb and rules, the journal + catalog, sysctl.d, binfmt.d, sysusers.d, tmpfiles.d. + +* Tue Feb 27 2018 Javier Martinez Canillas - 237-7.git84c8da5 +- Add patch to install kernel images for GRUB BootLoaderSpec support + +* Sat Feb 24 2018 Zbigniew Jędrzejewski-Szmek - 237-6.git84c8da5 +- Create /etc/systemd in %%post libs if necessary (#1548607) + +* Fri Feb 23 2018 Adam Williamson - 237-5.git84c8da5 +- Use : not touch to create file in -libs %%post + +* Thu Feb 22 2018 Patrick Uiterwijk - 237-4.git84c8da5 +- Add coreutils dep for systemd-libs %%post +- Add patch to typecast USB IDs to avoid compile failure + +* Wed Feb 21 2018 Zbigniew Jędrzejewski-Szmek - 237-3.git84c8da5 +- Update some patches for test skipping that were updated upstream + before merging +- Add /usr/lib/systemd/purge-nobody-user — a script to check if nobody is defined + correctly and possibly replace existing mappings + +* Tue Feb 20 2018 Zbigniew Jędrzejewski-Szmek - 237-2.gitdff4849 +- Backport a bunch of patches, most notably for the journal and various + memory issues. Some minor build fixes. +- Switch to new ldconfig macros that do nothing in F28+ +- /etc/systemd/dont-synthesize-nobody is created in %%post if nfsnobody + or nobody users are defined (#1537262) + +* Fri Feb 9 2018 Zbigniew Jędrzejeweski-Szmek - 237-1.git78bd769 +- Update to first stable snapshot (various minor memory leaks and misaccesses, + some documentation bugs, build fixes). + +* Sun Jan 28 2018 Zbigniew Jędrzejewski-Szmek - 237-1 +- Update to latest version + +* Sun Jan 21 2018 Björn Esser - 236-4.git3e14c4c +- Add patch to include if needed + +* Sat Jan 20 2018 Björn Esser - 236-3.git3e14c4c +- Rebuilt for switch to libxcrypt + +* Thu Jan 11 2018 Zbigniew Jędrzejewski-Szmek - 236-2.git23e14c4 +- Backport a bunch of bugfixes from upstream (#1531502, #1531381, #1526621 + various memory corruptions in systemd-networkd) +- /dev/kvm is marked as a static node which fixes permissions on s390x + and ppc64 (#1532382) + +* Fri Dec 15 2017 Zbigniew Jędrzejewski-Szmek - 236-1 +- Update to latest version + +* Mon Dec 11 2017 Zbigniew Jędrzejewski-Szmek - 235-5.git4a0e928 +- Update to latest git snapshot, do not build for realz +- Switch to libidn2 again (#1449145) + +* Tue Nov 07 2017 Zbigniew Jędrzejewski-Szmek - 235-4 +- Rebuild for cryptsetup-2.0.0-0.2.fc28 + +* Wed Oct 25 2017 Zbigniew Jędrzejewski-Szmek - 235-3 +- Backport a bunch of patches, including LP#172535 + +* Wed Oct 18 2017 Zbigniew Jędrzejewski-Szmek - 235-2 +- Patches for cryptsetup _netdev + +* Fri Oct 6 2017 Zbigniew Jędrzejewski-Szmek - 235-1 +- Update to latest version + +* Tue Sep 26 2017 Nathaniel McCallum - 234-8 +- Backport /etc/crypttab _netdev feature from upstream + +* Thu Sep 21 2017 Michal Sekletar - 234-7 +- Make sure to remove all device units sharing the same sysfs path (#1475570) + +* Mon Sep 18 2017 Zbigniew Jędrzejewski-Szmek - 234-6 +- Bump xslt recursion limit for libxslt-1.30 + +* Mon Jul 31 2017 Zbigniew Jędrzejewski-Szmek - 234-5 +- Backport more patches (#1476005, hopefully #1462378) + +* Thu Jul 27 2017 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Mon Jul 17 2017 Zbigniew Jędrzejewski-Szmek - 234-3 +- Fix x-systemd.timeout=0 in /etc/fstab (#1462378) +- Minor patches (memleaks, --help fixes, seccomp on arm64) + +* Thu Jul 13 2017 Zbigniew Jędrzejewski-Szmek - 234-2 +- Create kvm group (#1431876) + +* Thu Jul 13 2017 Zbigniew Jędrzejewski-Szmek - 234-1 +- Latest release + +* Sat Jul 1 2017 Zbigniew Jędrzejewski-Szmek - 233-7.git74d8f1c +- Update to snapshot +- Build with meson again + +* Tue Jun 27 2017 Zbigniew Jędrzejewski-Szmek - 233-6 +- Fix an out-of-bounds write in systemd-resolved (CVE-2017-9445) + +* Fri Jun 16 2017 Zbigniew Jędrzejewski-Szmek - 233-5.gitec36d05 +- Update to snapshot version, build with meson + +* Thu Jun 15 2017 Zbigniew Jędrzejewski-Szmek - 233-4 +- Backport a bunch of small fixes (memleaks, wrong format strings, + man page clarifications, shell completion) +- Fix systemd-resolved crash on crafted DNS packet (CVE-2017-9217, #1455493) +- Fix systemd-vconsole-setup.service error on systems with no VGA console (#1272686) +- Drop soft-static uid for systemd-journal-gateway +- Use ID from /etc/os-release as ntpvendor + +* Thu Mar 16 2017 Michal Sekletar - 233-3 +- Backport bugfixes from upstream +- Don't return error when machinectl couldn't figure out container IP addresses (#1419501) + +* Thu Mar 2 2017 Zbigniew Jędrzejewski-Szmek - 233-2 +- Fix installation conflict with polkit + +* Thu Mar 2 2017 Zbigniew Jędrzejewski-Szmek - 233-1 +- New upstream release (#1416201, #1405439, #1420753, many others) +- New systemd-tests subpackage with "installed tests" + +* Thu Feb 16 2017 Zbigniew Jędrzejewski-Szmek - 232-15 +- Add %%ghost %%dir entries for .wants dirs of our targets (#1422894) + +* Tue Feb 14 2017 Zbigniew Jędrzejewski-Szmek - 232-14 +- Ignore the hwdb parser test + +* Tue Feb 14 2017 Jan Synáček - 232-14 +- machinectl fails when virtual machine is running (#1419501) + +* Sat Feb 11 2017 Fedora Release Engineering - 232-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Tue Jan 31 2017 Zbigniew Jędrzejewski-Szmek - 232-12 +- Backport patch for initrd-switch-root.service getting killed (#1414904) +- Fix sd-journal-gatewayd -D, --trust, and COREDUMP_CONTAINER_CMDLINE + extraction by sd-coredump. + +* Sun Jan 29 2017 zbyszek - 232-11 +- Backport a number of patches (#1411299, #1413075, #1415745, + ##1415358, #1416588, #1408884) +- Fix various memleaks and unitialized variable access +- Shell completion enhancements +- Enable TPM logging by default (#1411156) +- Update hwdb (#1270124) + +* Thu Jan 19 2017 Adam Williamson - 232-10 +- Backport fix for boot failure in initrd-switch-root (#1414904) + +* Wed Jan 18 2017 Zbigniew Jędrzejewski-Szmek - 232-9 +- Add fake dependency on systemd-pam to systemd-devel to ensure systemd-pam + is available as multilib (#1414153) + +* Tue Jan 17 2017 Zbigniew Jędrzejewski-Szmek - 232-8 +- Fix buildsystem to check for lz4 correctly (#1404406) + +* Wed Jan 11 2017 Zbigniew Jędrzejewski-Szmek - 232-7 +- Various small tweaks to scriplets + +* Sat Jan 07 2017 Kevin Fenzi - 232-6 +- Fix scriptlets to never fail in libs post + +* Fri Jan 06 2017 Kevin Fenzi - 232-5 +- Add patch from Michal Schmidt to avoid process substitution (#1392236) + +* Sun Nov 6 2016 Zbigniew Jędrzejewski-Szmek - 232-4 +- Rebuild (#1392236) + +* Fri Nov 4 2016 Zbigniew Jędrzejewski-Szmek - 232-3 +- Make /etc/dbus-1/system.d directory non-%%ghost + +* Fri Nov 4 2016 Zbigniew Jędrzejewski-Szmek - 232-2 +- Fix kernel-install (#1391829) +- Restore previous systemd-user PAM config (#1391836) +- Move journal-upload.conf.5 from systemd main to journal-remote subpackage (#1391833) +- Fix permissions on /var/lib/systemd/journal-upload (#1262665) + +* Thu Nov 3 2016 Zbigniew Jędrzejewski-Szmek - 232-1 +- Update to latest version (#998615, #1181922, #1374371, #1390704, #1384150, #1287161) +- Add %%{_isa} to Provides on arch-full packages (#1387912) +- Create systemd-coredump user in %%pre (#1309574) +- Replace grubby patch with a short-circuiting install.d "plugin" +- Enable nss-systemd in the passwd, group lines in nsswith.conf +- Add [!UNAVAIL=return] fallback after nss-resolve in hosts line in nsswith.conf +- Move systemd-nspawn man pages to the right subpackage (#1391703) + +* Tue Oct 18 2016 Jan Synáček - 231-11 +- SPC - Cannot restart host operating from container (#1384523) + +* Sun Oct 9 2016 Zbigniew Jędrzejewski-Szmek - 231-10 +- Do not recreate /var/log/journal on upgrades (#1383066) +- Move nss-myhostname provides to systemd-libs (#1383271) + +* Fri Oct 7 2016 Zbigniew Jędrzejewski-Szmek - 231-9 +- Fix systemctl set-default (#1374371) +- Prevent systemd-udev-trigger.service from restarting (follow-up for #1378974) + +* Tue Oct 4 2016 Zbigniew Jędrzejewski-Szmek - 231-8 +- Apply fix for #1378974 + +* Mon Oct 3 2016 Zbigniew Jędrzejewski-Szmek - 231-7 +- Apply patches properly + +* Thu Sep 29 2016 Zbigniew Jędrzejewski-Szmek - 231-6 +- Better fix for (#1380286) + +* Thu Sep 29 2016 Zbigniew Jędrzejewski-Szmek - 231-5 +- Denial-of-service bug against pid1 (#1380286) + +* Thu Aug 25 2016 Zbigniew Jędrzejewski-Szmek - 231-4 +- Fix preset-all (#1363858) +- Fix issue with daemon-reload messing up graphics (#1367766) +- A few other bugfixes + +* Wed Aug 03 2016 Adam Williamson - 231-3 +- Revert preset-all change, it broke stuff (#1363858) + +* Wed Jul 27 2016 Zbigniew Jędrzejewski-Szmek - 231-2 +- Call preset-all on initial installation (#1118740) +- Fix botched Recommends for libxkbcommon + +* Tue Jul 26 2016 Zbigniew Jędrzejewski-Szmek - 231-1 +- Update to latest version + +* Wed Jun 8 2016 Zbigniew Jędrzejewski-Szmek - 230-3 +- Update to latest git snapshot (fixes for systemctl set-default, + polkit lingering policy, reversal of the framebuffer rules, + unaligned access fixes, fix for StartupBlockIOWeight-over-dbus). + Those changes are interspersed with other changes and new features + (mostly in lldp, networkd, and nspawn). Some of those new features + might not work, but I think that existing functionality should not + be broken, so it seems worthwile to update to the snapshot. + +* Sat May 21 2016 Zbigniew Jędrzejewski-Szmek - 230-2 +- Remove systemd-compat-libs on upgrade + +* Sat May 21 2016 Zbigniew Jędrzejewski-Szmek - 230-1 +- New version +- Drop compat-libs +- Require libxkbcommon explictly, since the automatic dependency will + not be generated anymore + +* Tue Apr 26 2016 Zbigniew Jędrzejewski-Szmek - 229-15 +- Remove duplicated entries in -container %%files (#1330395) + +* Fri Apr 22 2016 Zbigniew Jędrzejewski-Szmek - 229-14 +- Move installation of udev services to udev subpackage (#1329023) + +* Mon Apr 18 2016 Zbigniew Jędrzejewski-Szmek - 229-13 +- Split out systemd-pam subpackage (#1327402) + +* Mon Apr 18 2016 Harald Hoyer - 229-12 +- move more binaries and services from the main package to subpackages + +* Mon Apr 18 2016 Harald Hoyer - 229-11 +- move more binaries and services from the main package to subpackages + +* Mon Apr 18 2016 Harald Hoyer - 229-10 +- move device dependant stuff to the udev subpackage + +* Tue Mar 22 2016 Zbigniew Jędrzejewski-Szmek - 229-9 +- Add myhostname to /etc/nsswitch.conf (#1318303) + +* Mon Mar 21 2016 Harald Hoyer - 229-8 +- fixed kernel-install for copying files for grubby +Resolves: rhbz#1299019 + +* Thu Mar 17 2016 Zbigniew Jędrzejewski-Szmek - 229-7 +- Moar patches (#1316964, #1317928) +- Move vconsole-setup and tmpfiles-setup-dev bits to systemd-udev +- Protect systemd-udev from deinstallation + +* Fri Mar 11 2016 Zbigniew Jędrzejewski-Szmek - 229-6 +- Create /etc/resolv.conf symlink from systemd-resolved (#1313085) + +* Fri Mar 4 2016 Zbigniew Jędrzejewski-Szmek - 229-5 +- Split out systemd-container subpackage (#1163412) +- Split out system-udev subpackage +- Add various bugfix patches, incl. a tentative fix for #1308771 + +* Tue Mar 1 2016 Peter Robinson 229-4 +- Power64 and s390(x) now have libseccomp support +- aarch64 has gnu-efi + +* Tue Feb 23 2016 Jan Synáček - 229-3 +- Fix build failures on ppc64 (#1310800) + +* Tue Feb 16 2016 Dennis Gilmore - 229-2 +- revert: fixed kernel-install for copying files for grubby +Resolves: rhbz#1299019 +- this causes the dtb files to not get installed at all and the fdtdir +- line in extlinux.conf to not get updated correctly + +* Thu Feb 11 2016 Michal Sekletar - 229-1 +- New upstream release + +* Thu Feb 11 2016 Harald Hoyer - 228-10.gite35a787 +- fixed kernel-install for copying files for grubby +Resolves: rhbz#1299019 + +* Fri Feb 05 2016 Fedora Release Engineering - 228-9.gite35a787 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jan 27 2016 Peter Robinson 228-8.gite35a787 +- Rebuild for binutils on aarch64 fix + +* Fri Jan 08 2016 Dan Horák - 228-7.gite35a787 +- apply the conflict with fedora-release only in Fedora + +* Thu Dec 10 2015 Jan Synáček - 228-6.gite35a787 +- Fix rawhide build failures on ppc64 (#1286249) + +* Sun Nov 29 2015 Zbigniew Jędrzejewski-Szmek - 228-6.gite35a787 +- Create /etc/systemd/network (#1286397) + +* Thu Nov 26 2015 Zbigniew Jędrzejewski-Szmek - 228-5.gite35a787 +- Do not install nss modules by default + +* Tue Nov 24 2015 Zbigniew Jędrzejewski-Szmek - 228-4.gite35a787 +- Update to latest upstream git: there is a bunch of fixes + (nss-mymachines overflow bug, networkd fixes, more completions are + properly installed), mixed with some new resolved features. +- Rework file triggers so that they always run before daemons are restarted + +* Thu Nov 19 2015 Zbigniew Jędrzejewski-Szmek - 228-3 +- Enable rpm file triggers for daemon-reload + +* Thu Nov 19 2015 Zbigniew Jędrzejewski-Szmek - 228-2 +- Fix version number in obsoleted package name (#1283452) + +* Wed Nov 18 2015 Kay Sievers - 228-1 +- New upstream release + +* Thu Nov 12 2015 Zbigniew Jędrzejewski-Szmek - 227-7 +- Rename journal-gateway subpackage to journal-remote +- Ignore the access mode on /var/log/journal (#1048424) +- Do not assume fstab is present (#1281606) + +* Wed Nov 11 2015 Fedora Release Engineering - 227-6 +- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5 + +* Tue Nov 10 2015 Lukáš Nykrýn - 227-5 +- Rebuild for libmicrohttpd soname bump + +* Fri Nov 06 2015 Robert Kuska - 227-4 +- Rebuilt for Python3.5 rebuild + +* Wed Nov 4 2015 Zbigniew Jędrzejewski-Szmek - 227-3 +- Fix syntax in kernel-install (#1277264) + +* Tue Nov 03 2015 Michal Schmidt - 227-2 +- Rebuild for libmicrohttpd soname bump. + +* Wed Oct 7 2015 Kay Sievers - 227-1 +- New upstream release + +* Fri Sep 18 2015 Jan Synáček - 226-3 +- user systemd-journal-upload should be in systemd-journal group (#1262743) + +* Fri Sep 18 2015 Kay Sievers - 226-2 +- Add selinux to system-user PAM config + +* Tue Sep 8 2015 Kay Sievers - 226-1 +- New upstream release + +* Thu Aug 27 2015 Kay Sievers - 225-1 +- New upstream release + +* Fri Jul 31 2015 Kay Sievers - 224-1 +- New upstream release + +* Wed Jul 29 2015 Kay Sievers - 223-2 +- update to git snapshot + +* Wed Jul 29 2015 Kay Sievers - 223-1 +- New upstream release + +* Thu Jul 9 2015 Zbigniew Jędrzejewski-Szmek - 222-2 +- Remove python subpackages (python-systemd in now standalone) + +* Tue Jul 7 2015 Kay Sievers - 222-1 +- New upstream release + +* Mon Jul 6 2015 Kay Sievers - 221-5.git619b80a +- update to git snapshot + +* Mon Jul 6 2015 Zbigniew Jędrzejewski-Szmek - 221-4.git604f02a +- Add example file with yama config (#1234951) + +* Sun Jul 5 2015 Kay Sievers - 221-3.git604f02a +- update to git snapshot + +* Mon Jun 22 2015 Kay Sievers - 221-2 +- build systemd-boot EFI tools + +* Fri Jun 19 2015 Lennart Poettering - 221-1 +- New upstream release +- Undoes botched translation check, should be reinstated later? + +* Fri Jun 19 2015 Fedora Release Engineering - 220-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Thu Jun 11 2015 Peter Robinson 220-9 +- The gold linker is now fixed on aarch64 + +* Tue Jun 9 2015 Zbigniew Jędrzejewski-Szmek - 220-8 +- Remove gudev which is now provided as separate package (libgudev) +- Fix for spurious selinux denials (#1224211) +- Udev change events (#1225905) +- Patches for some potential crashes +- ProtectSystem=yes does not touch /home +- Man page fixes, hwdb updates, shell completion updates +- Restored persistent device symlinks for bcache, xen block devices +- Tag all DRM cards as master-of-seat + +* Tue Jun 09 2015 Harald Hoyer 220-7 +- fix udev block device watch + +* Tue Jun 09 2015 Harald Hoyer 220-6 +- add support for network disk encryption + +* Sun Jun 7 2015 Peter Robinson 220-5 +- Disable gold on aarch64 until it's fixed (tracked in rhbz #1225156) + +* Sat May 30 2015 Zbigniew Jędrzejewski-Szmek - 220-4 +- systemd-devel should require systemd-libs, not the main package (#1226301) +- Check for botched translations (#1226566) +- Make /etc/udev/hwdb.d part of the rpm (#1226379) + +* Thu May 28 2015 Richard W.M. Jones - 220-3 +- Add patch to fix udev --daemon not cleaning child processes + (upstream commit 86c3bece38bcf5). + +* Wed May 27 2015 Richard W.M. Jones - 220-2 +- Add patch to fix udev --daemon crash (upstream commit 040e689654ef08). + +* Thu May 21 2015 Lennart Poettering - 220-1 +- New upstream release +- Drop /etc/mtab hack, as that's apparently fixed in mock now (#1116158) +- Remove ghosting for %%{_sysconfdir}/systemd/system/runlevel*.target, these targets are not configurable anymore in systemd upstream +- Drop work-around for #1002806, since this is solved upstream now + +* Wed May 20 2015 Dennis Gilmore - 219-15 +- fix up the conflicts version for fedora-release + +* Wed May 20 2015 Zbigniew Jędrzejewski-Szmek - 219-14 +- Remove presets (#1221340) +- Fix (potential) crash and memory leak in timedated, locking failure + in systemd-nspawn, crash in resolved. +- journalctl --list-boots should be faster +- zsh completions are improved +- various ommissions in docs are corrected (#1147651) +- VARIANT and VARIANT_ID fields in os-release are documented +- systemd-fsck-root.service is generated in the initramfs (#1201979, #1107818) +- systemd-tmpfiles should behave better on read-only file systems (#1207083) + +* Wed Apr 29 2015 Zbigniew Jędrzejewski-Szmek - 219-13 +- Patches for some outstanding annoyances +- Small keyboard hwdb updates + +* Wed Apr 8 2015 Zbigniew Jędrzejewski-Szmek - 219-12 +- Tighten requirements between subpackages (#1207381). + +* Sun Mar 22 2015 Zbigniew Jędrzejewski-Szmek - 219-11 +- Move all parts systemd-journal-{remote,upload} to + systemd-journal-gatewayd subpackage (#1193143). +- Create /var/lib/systemd/journal-upload directory (#1193145). +- Cut out lots of stupid messages at debug level which were obscuring more + important stuff. +- Apply "tentative" state for devices only when they are added, not removed. +- Ignore invalid swap pri= settings (#1204336) +- Fix SELinux check for timedated operations to enable/disable ntp (#1014315) +- Fix comparing of filesystem paths (#1184016) + +* Sat Mar 14 2015 Zbigniew Jędrzejewski-Szmek - 219-10 +- Fixes for bugs 1186018, 1195294, 1185604, 1196452. +- Hardware database update. +- Documentation fixes. +- A fix for journalctl performance regression. +- Fix detection of inability to open files in journalctl. +- Detect SuperH architecture properly. +- The first of duplicate lines in tmpfiles wins again. +- Do vconsole setup after loading vconsole driver, not fbcon. +- Fix problem where some units were restarted during systemd reexec. +- Fix race in udevadm settle tripping up NetworkManager. +- Downgrade various log messages. +- Fix issue where journal-remote would process some messages with a delay. +- GPT /srv partition autodiscovery is fixed. +- Reconfigure old Finnish keymaps in post (#1151958) + +* Tue Mar 10 2015 Jan Synáček - 219-9 +- Buttons on Lenovo X6* tablets broken (#1198939) + +* Tue Mar 3 2015 Zbigniew Jędrzejewski-Szmek - 219-8 +- Reworked device handling (#1195761) +- ACL handling fixes (with a script in %%post) +- Various log messages downgraded (#1184712) +- Allow PIE on s390 again (#1197721) + +* Wed Feb 25 2015 Michal Schmidt - 219-7 +- arm: reenable lto. gcc-5.0.0-0.16 fixed the crash (#1193212) + +* Tue Feb 24 2015 Colin Walters - 219-6 +- Revert patch that breaks Atomic/OSTree (#1195761) + +* Fri Feb 20 2015 Michal Schmidt - 219-5 +- Undo the resolv.conf workaround, Aim for a proper fix in Rawhide. + +* Fri Feb 20 2015 Michal Schmidt - 219-4 +- Revive fedora-disable-resolv.conf-symlink.patch to unbreak composes. + +* Wed Feb 18 2015 Michal Schmidt - 219-3 +- arm: disabling gold did not help; disable lto instead (#1193212) + +* Tue Feb 17 2015 Peter Jones - 219-2 +- Update 90-default.present for dbxtool. + +* Mon Feb 16 2015 Lennart Poettering - 219-1 +- New upstream release +- This removes the sysctl/bridge hack, a different solution needs to be found for this (see #634736) +- This removes the /etc/resolv.conf hack, anaconda needs to fix their handling of /etc/resolv.conf as symlink +- This enables "%%check" +- disable gold on arm, as that is broken (see #1193212) + +* Mon Feb 16 2015 Peter Robinson 218-6 +- aarch64 now has seccomp support + +* Thu Feb 05 2015 Michal Schmidt - 218-5 +- Don't overwrite systemd.macros with unrelated Source file. + +* Thu Feb 5 2015 Jan Synáček - 218-4 +- Add a touchpad hwdb (#1189319) + +* Thu Jan 15 2015 Zbigniew Jędrzejewski-Szmek - 218-4 +- Enable xkbcommon dependency to allow checking of keymaps +- Fix permissions of /var/log/journal (#1048424) +- Enable timedatex in presets (#1187072) +- Disable rpcbind in presets (#1099595) + +* Wed Jan 7 2015 Jan Synáček - 218-3 +- RFE: journal: automatically rotate the file if it is unlinked (#1171719) + +* Mon Jan 05 2015 Zbigniew Jędrzejewski-Szmek - 218-3 +- Add firewall description files (#1176626) + +* Thu Dec 18 2014 Jan Synáček - 218-2 +- systemd-nspawn doesn't work on s390/s390x (#1175394) + +* Wed Dec 10 2014 Lennart Poettering - 218-1 +- New upstream release +- Enable "nss-mymachines" in /etc/nsswitch.conf + +* Thu Nov 06 2014 Zbigniew Jędrzejewski-Szmek - 217-4 +- Change libgudev1 to only require systemd-libs (#727499), there's + no need to require full systemd stack. +- Fixes for bugs #1159448, #1152220, #1158035. +- Bash completions updates to allow propose more units for start/restart, + and completions for set-default,get-default. +- Again allow systemctl enable of instances. +- Hardware database update and fixes. +- Udev crash on invalid options and kernel commandline timeout parsing are fixed. +- Add "embedded" chassis type. +- Sync before 'reboot -f'. +- Fix restarting of timer units. + +* Wed Nov 05 2014 Michal Schmidt - 217-3 +- Fix hanging journal flush (#1159641) + +* Fri Oct 31 2014 Michal Schmidt - 217-2 +- Fix ordering cycles involving systemd-journal-flush.service and + remote-fs.target (#1159117) + +* Tue Oct 28 2014 Lennart Poettering - 217-1 +- New upstream release + +* Fri Oct 17 2014 Zbigniew Jędrzejewski-Szmek - 216-12 +- Drop PackageKit.service from presets (#1154126) + +* Mon Oct 13 2014 Zbigniew Jędrzejewski-Szmek - 216-11 +- Conflict with old versions of initscripts (#1152183) +- Remove obsolete Finnish keymap (#1151958) + +* Fri Oct 10 2014 Zbigniew Jędrzejewski-Szmek - 216-10 +- Fix a problem with voluntary daemon exits and some other bugs + (#1150477, #1095962, #1150289) + +* Fri Oct 03 2014 Zbigniew Jędrzejewski-Szmek - 216-9 +- Update to latest git, but without the readahead removal patch + (#1114786, #634736) + +* Wed Oct 01 2014 Kay Sievers - 216-8 +- revert "don't reset selinux context during CHANGE events" + +* Wed Oct 01 2014 Lukáš Nykrýn - 216-7 +- add temporary workaround for #1147910 +- don't reset selinux context during CHANGE events + +* Wed Sep 10 2014 Michal Schmidt - 216-6 +- Update timesyncd with patches to avoid hitting NTP pool too often. + +* Tue Sep 09 2014 Michal Schmidt - 216-5 +- Use common CONFIGURE_OPTS for build2 and build3. +- Configure timesyncd with NTP servers from Fedora/RHEL vendor zone. + +* Wed Sep 03 2014 Zbigniew Jędrzejewski-Szmek - 216-4 +- Move config files for sd-j-remote/upload to sd-journal-gateway subpackage (#1136580) + +* Thu Aug 28 2014 Peter Robinson 216-3 +- Drop no LTO build option for aarch64/s390 now it's fixed in binutils (RHBZ 1091611) + +* Thu Aug 21 2014 Zbigniew Jędrzejewski-Szmek - 216-2 +- Re-add patch to disable resolve.conf symlink (#1043119) + +* Wed Aug 20 2014 Lennart Poettering - 216-1 +- New upstream release + +* Mon Aug 18 2014 Fedora Release Engineering - 215-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Wed Aug 13 2014 Dan Horák 215-11 +- disable LTO also on s390(x) + +* Sat Aug 09 2014 Harald Hoyer 215-10 +- fixed PPC64LE + +* Wed Aug 6 2014 Tom Callaway - 215-9 +- fix license handling + +* Wed Jul 30 2014 Zbigniew Jędrzejewski-Szmek - 215-8 +- Create systemd-journal-remote and systemd-journal-upload users (#1118907) + +* Thu Jul 24 2014 Zbigniew Jędrzejewski-Szmek - 215-7 +- Split out systemd-compat-libs subpackage + +* Tue Jul 22 2014 Kalev Lember - 215-6 +- Rebuilt for gobject-introspection 1.41.4 + +* Mon Jul 21 2014 Zbigniew Jędrzejewski-Szmek - 215-5 +- Fix SELinux context of /etc/passwd-, /etc/group-, /etc/.updated (#1121806) +- Add missing BR so gnutls and elfutils are used + +* Sat Jul 19 2014 Zbigniew Jędrzejewski-Szmek - 215-4 +- Various man page updates +- Static device node logic is conditionalized on CAP_SYS_MODULES instead of CAP_MKNOD + for better behaviour in containers +- Some small networkd link handling fixes +- vconsole-setup runs setfont before loadkeys (https://bugs.freedesktop.org/show_bug.cgi?id=80685) +- New systemd-escape tool +- XZ compression settings are tweaked to greatly improve journald performance +- "watch" is accepted as chassis type +- Various sysusers fixes, most importantly correct selinux labels +- systemd-timesyncd bug fix (https://bugs.freedesktop.org/show_bug.cgi?id=80932) +- Shell completion improvements +- New udev tag ID_SOFTWARE_RADIO can be used to instruct logind to allow user access +- XEN and s390 virtualization is properly detected + +* Mon Jul 07 2014 Colin Walters - 215-3 +- Add patch to disable resolve.conf symlink (#1043119) + +* Sun Jul 06 2014 Zbigniew Jędrzejewski-Szmek - 215-2 +- Move systemd-journal-remote to systemd-journal-gateway package (#1114688) +- Disable /etc/mtab handling temporarily (#1116158) + +* Thu Jul 03 2014 Lennart Poettering - 215-1 +- New upstream release +- Enable coredump logic (which abrt would normally override) + +* Sun Jun 29 2014 Peter Robinson 214-5 +- On aarch64 disable LTO as it still has issues on that arch + +* Thu Jun 26 2014 Zbigniew Jędrzejewski-Szmek - 214-4 +- Bugfixes (#996133, #1112908) + +* Mon Jun 23 2014 Zbigniew Jędrzejewski-Szmek - 214-3 +- Actually create input group (#1054549) + +* Sun Jun 22 2014 Zbigniew Jędrzejewski-Szmek - 214-2 +- Do not restart systemd-logind on upgrades (#1110697) +- Add some patches (#1081429, #1054549, #1108568, #928962) + +* Wed Jun 11 2014 Lennart Poettering - 214-1 +- New upstream release +- Get rid of "floppy" group, since udev uses "disk" now +- Reenable LTO + +* Sun Jun 08 2014 Fedora Release Engineering - 213-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed May 28 2014 Kay Sievers - 213-3 +- fix systemd-timesync user creation + +* Wed May 28 2014 Michal Sekletar - 213-2 +- Create temporary files after installation (#1101983) +- Add sysstat-collect.timer, sysstat-summary.timer to preset policy (#1101621) + +* Wed May 28 2014 Kay Sievers - 213-1 +- New upstream release + +* Tue May 27 2014 Kalev Lember - 212-6 +- Rebuilt for https://fedoraproject.org/wiki/Changes/Python_3.4 + +* Fri May 23 2014 Adam Williamson - 212-5 +- revert change from 212-4, causes boot fail on single CPU boxes (RHBZ 1095891) + +* Wed May 07 2014 Kay Sievers - 212-4 +- add netns udev workaround + +* Wed May 07 2014 Michal Sekletar - 212-3 +- enable uuidd.socket by default (#1095353) + +* Sat Apr 26 2014 Peter Robinson 212-2 +- Disable building with -flto for the moment due to gcc 4.9 issues (RHBZ 1091611) + +* Tue Mar 25 2014 Lennart Poettering - 212-1 +- New upstream release + +* Mon Mar 17 2014 Peter Robinson 211-2 +- Explicitly define which upstream platforms support libseccomp + +* Tue Mar 11 2014 Lennart Poettering - 211-1 +- New upstream release + +* Mon Mar 10 2014 Zbigniew Jędrzejewski-Szmek - 210-8 +- Fix logind unpriviledged reboot issue and a few other minor fixes +- Limit generator execution time +- Recognize buttonless joystick types + +* Fri Mar 07 2014 Karsten Hopp 210-7 +- ppc64le needs link warnings disabled, too + +* Fri Mar 07 2014 Karsten Hopp 210-6 +- move ifarch ppc64le to correct place (libseccomp req) + +* Fri Mar 07 2014 Zbigniew Jędrzejewski-Szmek - 210-5 +- Bugfixes: #1047568, #1047039, #1071128, #1073402 +- Bash completions for more systemd tools +- Bluetooth database update +- Manpage fixes + +* Thu Mar 06 2014 Zbigniew Jędrzejewski-Szmek - 210-4 +- Apply work-around for ppc64le too (#1073647). + +* Sat Mar 01 2014 Zbigniew Jędrzejewski-Szmek - 210-3 +- Backport a few patches, add completion for systemd-nspawn. + +* Fri Feb 28 2014 Zbigniew Jędrzejewski-Szmek - 210-3 +- Apply work-arounds for ppc/ppc64 for bugs 1071278 and 1071284 + +* Mon Feb 24 2014 Lennart Poettering - 210-2 +- Check more services against preset list and enable by default + +* Mon Feb 24 2014 Lennart Poettering - 210-1 +- new upstream release + +* Sun Feb 23 2014 Zbigniew Jędrzejewski-Szmek - 209-2.gitf01de96 +- Enable dnssec-triggerd.service by default (#1060754) + +* Sun Feb 23 2014 Kay Sievers - 209-2.gitf01de96 +- git snapshot to sort out ARM build issues + +* Thu Feb 20 2014 Lennart Poettering - 209-1 +- new upstream release + +* Tue Feb 18 2014 Zbigniew Jędrzejewski-Szmek - 208-15 +- Make gpsd lazily activated (#1066421) + +* Mon Feb 17 2014 Zbigniew Jędrzejewski-Szmek - 208-14 +- Back out patch which causes user manager to be destroyed when unneeded + and spams logs (#1053315) + +* Sun Feb 16 2014 Zbigniew Jędrzejewski-Szmek - 208-13 +- A different fix for #1023820 taken from Mageia +- Backported fix for #997031 +- Hardward database updates, man pages improvements, a few small memory + leaks, utf-8 correctness and completion fixes +- Support for key-slot option in crypttab + +* Sat Jan 25 2014 Ville Skyttä - 208-12 +- Own the %%{_prefix}/lib/kernel(/*) and %%{_datadir}/zsh(/*) dirs. + +* Tue Dec 03 2013 Zbigniew Jędrzejewski-Szmek - 208-11 +- Backport a few fixes, relevant documentation updates, and HWDB changes + (#1051797, #1051768, #1047335, #1047304, #1047186, #1045849, #1043304, + #1043212, #1039351, #1031325, #1023820, #1017509, #953077) +- Flip journalctl to --full by default (#984758) + +* Tue Dec 03 2013 Zbigniew Jędrzejewski-Szmek - 208-9 +- Apply two patches for #1026860 + +* Tue Dec 03 2013 Zbigniew Jędrzejewski-Szmek - 208-8 +- Bump release to stay ahead of f20 + +* Tue Dec 03 2013 Zbigniew Jędrzejewski-Szmek - 208-7 +- Backport patches (#1023041, #1036845, #1006386?) +- HWDB update +- Some small new features: nspawn --drop-capability=, running PID 1 under + valgrind, "yearly" and "annually" in calendar specifications +- Some small documentation and logging updates + +* Tue Nov 19 2013 Zbigniew Jędrzejewski-Szmek - 208-6 +- Bump release to stay ahead of f20 + +* Tue Nov 19 2013 Zbigniew Jędrzejewski-Szmek - 208-5 +- Use unit name in PrivateTmp= directories (#957439) +- Update manual pages, completion scripts, and hardware database +- Configurable Timeouts/Restarts default values +- Support printing of timestamps on the console +- Fix some corner cases in detecting when writing to the console is safe +- Python API: convert keyword values to string, fix sd_is_booted() wrapper +- Do not tread missing /sbin/fsck.btrfs as an error (#1015467) +- Allow masking of fsck units +- Advertise hibernation to swap files +- Fix SO_REUSEPORT settings +- Prefer converted xkb keymaps to legacy keymaps (#981805, #1026872) +- Make use of newer kmod +- Assorted bugfixes: #1017161, #967521, #988883, #1027478, #821723, #1014303 + +* Tue Oct 22 2013 Zbigniew Jędrzejewski-Szmek - 208-4 +- Add temporary fix for #1002806 + +* Mon Oct 21 2013 Zbigniew Jędrzejewski-Szmek - 208-3 +- Backport a bunch of fixes and hwdb updates + +* Wed Oct 2 2013 Lennart Poettering - 208-2 +- Move old random seed and backlight files into the right place + +* Wed Oct 2 2013 Lennart Poettering - 208-1 +- New upstream release + +* Thu Sep 26 2013 Zbigniew Jędrzejewski-Szmek 207-5 +- Do not create /var/var/... dirs + +* Wed Sep 18 2013 Zbigniew Jędrzejewski-Szmek 207-4 +- Fix policykit authentication +- Resolves: rhbz#1006680 + +* Tue Sep 17 2013 Harald Hoyer 207-3 +- fixed login +- Resolves: rhbz#1005233 + +* Mon Sep 16 2013 Harald Hoyer 207-2 +- add some upstream fixes for 207 +- fixed swap activation +- Resolves: rhbz#1008604 + +* Fri Sep 13 2013 Lennart Poettering - 207-1 +- New upstream release + +* Fri Sep 06 2013 Harald Hoyer 206-11 +- support "debug" kernel command line parameter +- journald: fix fd leak in journal_file_empty +- journald: fix vacuuming of archived journals +- libudev: enumerate - do not try to match against an empty subsystem +- cgtop: fixup the online help +- libudev: fix memleak when enumerating childs + +* Wed Sep 04 2013 Harald Hoyer 206-10 +- Do not require grubby, lorax now takes care of grubby +- cherry-picked a lot of patches from upstream + +* Tue Aug 27 2013 Dennis Gilmore - 206-9 +- Require grubby, Fedora installs require grubby, +- kernel-install took over from new-kernel-pkg +- without the Requires we are unable to compose Fedora +- everyone else says that since kernel-install took over +- it is responsible for ensuring that grubby is in place +- this is really what we want for Fedora + +* Tue Aug 27 2013 Kay Sievers - 206-8 +- Revert "Require grubby its needed by kernel-install" + +* Mon Aug 26 2013 Dennis Gilmore 206-7 +- Require grubby its needed by kernel-install + +* Thu Aug 22 2013 Harald Hoyer 206-6 +- kernel-install now understands kernel flavors like PAE + +* Tue Aug 20 2013 Rex Dieter - 206-5 +- add sddm.service to preset file (#998978) + +* Fri Aug 16 2013 Zbigniew Jędrzejewski-Szmek - 206-4 +- Filter out provides for private python modules. +- Add requires on kmod >= 14 (#990994). + +* Sun Aug 11 2013 Zbigniew Jedrzejewski-Szmek - 206-3 +- New systemd-python3 package (#976427). +- Add ownership of a few directories that we create (#894202). + +* Sun Aug 04 2013 Fedora Release Engineering - 206-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jul 23 2013 Kay Sievers - 206-1 +- New upstream release + Resolves (#984152) + +* Wed Jul 3 2013 Lennart Poettering - 205-1 +- New upstream release + +* Wed Jun 26 2013 Michal Schmidt 204-10 +- Split systemd-journal-gateway subpackage (#908081). + +* Mon Jun 24 2013 Michal Schmidt 204-9 +- Rename nm_dispatcher to NetworkManager-dispatcher in default preset (#977433) + +* Fri Jun 14 2013 Harald Hoyer 204-8 +- fix, which helps to sucessfully browse journals with + duplicated seqnums + +* Fri Jun 14 2013 Harald Hoyer 204-7 +- fix duplicate message ID bug +Resolves: rhbz#974132 + +* Thu Jun 06 2013 Harald Hoyer 204-6 +- introduce 99-default-disable.preset + +* Thu Jun 6 2013 Lennart Poettering - 204-5 +- Rename 90-display-manager.preset to 85-display-manager.preset so that it actually takes precedence over 90-default.preset's "disable *" line (#903690) + +* Tue May 28 2013 Harald Hoyer 204-4 +- Fix kernel-install (#965897) + +* Wed May 22 2013 Kay Sievers - 204-3 +- Fix kernel-install (#965897) + +* Thu May 9 2013 Lennart Poettering - 204-2 +- New upstream release +- disable isdn by default (#959793) + +* Tue May 07 2013 Harald Hoyer 203-2 +- forward port kernel-install-grubby.patch + +* Tue May 7 2013 Lennart Poettering - 203-1 +- New upstream release + +* Wed Apr 24 2013 Harald Hoyer 202-3 +- fix ENOENT for getaddrinfo +- Resolves: rhbz#954012 rhbz#956035 +- crypt-setup-generator: correctly check return of strdup +- logind-dbus: initialize result variable +- prevent library underlinking + +* Fri Apr 19 2013 Harald Hoyer 202-2 +- nspawn create empty /etc/resolv.conf if necessary +- python wrapper: add sd_journal_add_conjunction() +- fix s390 booting +- Resolves: rhbz#953217 + +* Thu Apr 18 2013 Lennart Poettering - 202-1 +- New upstream release + +* Tue Apr 09 2013 Michal Schmidt - 201-2 +- Automatically discover whether to run autoreconf and add autotools and git + BuildRequires based on the presence of patches to be applied. +- Use find -delete. + +* Mon Apr 8 2013 Lennart Poettering - 201-1 +- New upstream release + +* Mon Apr 8 2013 Lennart Poettering - 200-4 +- Update preset file + +* Fri Mar 29 2013 Lennart Poettering - 200-3 +- Remove NetworkManager-wait-online.service from presets file again, it should default to off + +* Fri Mar 29 2013 Lennart Poettering - 200-2 +- New upstream release + +* Tue Mar 26 2013 Lennart Poettering - 199-2 +- Add NetworkManager-wait-online.service to the presets file + +* Tue Mar 26 2013 Lennart Poettering - 199-1 +- New upstream release + +* Mon Mar 18 2013 Michal Schmidt 198-7 +- Drop /usr/s?bin/ prefixes. + +* Fri Mar 15 2013 Harald Hoyer 198-6 +- run autogen to pickup all changes + +* Fri Mar 15 2013 Harald Hoyer 198-5 +- do not mount anything, when not running as pid 1 +- add initrd.target for systemd in the initrd + +* Wed Mar 13 2013 Harald Hoyer 198-4 +- fix switch-root and local-fs.target problem +- patch kernel-install to use grubby, if available + +* Fri Mar 08 2013 Harald Hoyer 198-3 +- add Conflict with dracut < 026 because of the new switch-root isolate + +* Thu Mar 7 2013 Lennart Poettering - 198-2 +- Create required users + +* Thu Mar 7 2013 Lennart Poettering - 198-1 +- New release +- Enable journal persistancy by default + +* Sun Feb 10 2013 Peter Robinson 197-3 +- Bump for ARM + +* Fri Jan 18 2013 Michal Schmidt - 197-2 +- Added qemu-guest-agent.service to presets (Lennart, #885406). +- Add missing pygobject3-base to systemd-analyze deps (Lennart). +- Do not require hwdata, it is all in the hwdb now (Kay). +- Drop dependency on dbus-python. + +* Tue Jan 8 2013 Lennart Poettering - 197-1 +- New upstream release + +* Mon Dec 10 2012 Michal Schmidt - 196-4 +- Enable rngd.service by default (#857765). + +* Mon Dec 10 2012 Michal Schmidt - 196-3 +- Disable hardening on s390(x) because PIE is broken there and produces + text relocations with __thread (#868839). + +* Wed Dec 05 2012 Michal Schmidt - 196-2 +- added spice-vdagentd.service to presets (Lennart, #876237) +- BR cryptsetup-devel instead of the legacy cryptsetup-luks-devel provide name + (requested by Milan Brož). +- verbose make to see the actual build flags + +* Wed Nov 21 2012 Lennart Poettering - 196-1 +- New upstream release + +* Tue Nov 20 2012 Lennart Poettering - 195-8 +- https://bugzilla.redhat.com/show_bug.cgi?id=873459 +- https://bugzilla.redhat.com/show_bug.cgi?id=878093 + +* Thu Nov 15 2012 Michal Schmidt - 195-7 +- Revert udev killing cgroup patch for F18 Beta. +- https://bugzilla.redhat.com/show_bug.cgi?id=873576 + +* Fri Nov 09 2012 Michal Schmidt - 195-6 +- Fix cyclical dep between systemd and systemd-libs. +- Avoid broken build of test-journal-syslog. +- https://bugzilla.redhat.com/show_bug.cgi?id=873387 +- https://bugzilla.redhat.com/show_bug.cgi?id=872638 + +* Thu Oct 25 2012 Kay Sievers - 195-5 +- require 'sed', limit HOSTNAME= match + +* Wed Oct 24 2012 Michal Schmidt - 195-4 +- add dmraid-activation.service to the default preset +- add yum protected.d fragment +- https://bugzilla.redhat.com/show_bug.cgi?id=869619 +- https://bugzilla.redhat.com/show_bug.cgi?id=869717 + +* Wed Oct 24 2012 Kay Sievers - 195-3 +- Migrate /etc/sysconfig/ i18n, keyboard, network files/variables to + systemd native files + +* Tue Oct 23 2012 Lennart Poettering - 195-2 +- Provide syslog because the journal is fine as a syslog implementation + +* Tue Oct 23 2012 Lennart Poettering - 195-1 +- New upstream release +- https://bugzilla.redhat.com/show_bug.cgi?id=831665 +- https://bugzilla.redhat.com/show_bug.cgi?id=847720 +- https://bugzilla.redhat.com/show_bug.cgi?id=858693 +- https://bugzilla.redhat.com/show_bug.cgi?id=863481 +- https://bugzilla.redhat.com/show_bug.cgi?id=864629 +- https://bugzilla.redhat.com/show_bug.cgi?id=864672 +- https://bugzilla.redhat.com/show_bug.cgi?id=864674 +- https://bugzilla.redhat.com/show_bug.cgi?id=865128 +- https://bugzilla.redhat.com/show_bug.cgi?id=866346 +- https://bugzilla.redhat.com/show_bug.cgi?id=867407 +- https://bugzilla.redhat.com/show_bug.cgi?id=868603 + +* Wed Oct 10 2012 Michal Schmidt - 194-2 +- Add scriptlets for migration away from systemd-timedated-ntp.target + +* Wed Oct 3 2012 Lennart Poettering - 194-1 +- New upstream release +- https://bugzilla.redhat.com/show_bug.cgi?id=859614 +- https://bugzilla.redhat.com/show_bug.cgi?id=859655 + +* Fri Sep 28 2012 Lennart Poettering - 193-1 +- New upstream release + +* Tue Sep 25 2012 Lennart Poettering - 192-1 +- New upstream release + +* Fri Sep 21 2012 Lennart Poettering - 191-2 +- Fix journal mmap header prototype definition to fix compilation on 32bit + +* Fri Sep 21 2012 Lennart Poettering - 191-1 +- New upstream release +- Enable all display managers by default, as discussed with Adam Williamson + +* Thu Sep 20 2012 Lennart Poettering - 190-1 +- New upstream release +- Take possession of /etc/localtime, and remove /etc/sysconfig/clock +- https://bugzilla.redhat.com/show_bug.cgi?id=858780 +- https://bugzilla.redhat.com/show_bug.cgi?id=858787 +- https://bugzilla.redhat.com/show_bug.cgi?id=858771 +- https://bugzilla.redhat.com/show_bug.cgi?id=858754 +- https://bugzilla.redhat.com/show_bug.cgi?id=858746 +- https://bugzilla.redhat.com/show_bug.cgi?id=858266 +- https://bugzilla.redhat.com/show_bug.cgi?id=858224 +- https://bugzilla.redhat.com/show_bug.cgi?id=857670 +- https://bugzilla.redhat.com/show_bug.cgi?id=856975 +- https://bugzilla.redhat.com/show_bug.cgi?id=855863 +- https://bugzilla.redhat.com/show_bug.cgi?id=851970 +- https://bugzilla.redhat.com/show_bug.cgi?id=851275 +- https://bugzilla.redhat.com/show_bug.cgi?id=851131 +- https://bugzilla.redhat.com/show_bug.cgi?id=847472 +- https://bugzilla.redhat.com/show_bug.cgi?id=847207 +- https://bugzilla.redhat.com/show_bug.cgi?id=846483 +- https://bugzilla.redhat.com/show_bug.cgi?id=846085 +- https://bugzilla.redhat.com/show_bug.cgi?id=845973 +- https://bugzilla.redhat.com/show_bug.cgi?id=845194 +- https://bugzilla.redhat.com/show_bug.cgi?id=845028 +- https://bugzilla.redhat.com/show_bug.cgi?id=844630 +- https://bugzilla.redhat.com/show_bug.cgi?id=839736 +- https://bugzilla.redhat.com/show_bug.cgi?id=835848 +- https://bugzilla.redhat.com/show_bug.cgi?id=831740 +- https://bugzilla.redhat.com/show_bug.cgi?id=823485 +- https://bugzilla.redhat.com/show_bug.cgi?id=821813 +- https://bugzilla.redhat.com/show_bug.cgi?id=807886 +- https://bugzilla.redhat.com/show_bug.cgi?id=802198 +- https://bugzilla.redhat.com/show_bug.cgi?id=767795 +- https://bugzilla.redhat.com/show_bug.cgi?id=767561 +- https://bugzilla.redhat.com/show_bug.cgi?id=752774 +- https://bugzilla.redhat.com/show_bug.cgi?id=732874 +- https://bugzilla.redhat.com/show_bug.cgi?id=858735 + +* Thu Sep 13 2012 Lennart Poettering - 189-4 +- Don't pull in pkg-config as dep +- https://bugzilla.redhat.com/show_bug.cgi?id=852828 + +* Wed Sep 12 2012 Lennart Poettering - 189-3 +- Update preset policy +- Rename preset policy file from 99-default.preset to 90-default.preset so that people can order their own stuff after the Fedora default policy if they wish + +* Thu Aug 23 2012 Lennart Poettering - 189-2 +- Update preset policy +- https://bugzilla.redhat.com/show_bug.cgi?id=850814 + +* Thu Aug 23 2012 Lennart Poettering - 189-1 +- New upstream release + +* Thu Aug 16 2012 Ray Strode 188-4 +- more scriptlet fixes + (move dm migration logic to %%posttrans so the service + files it's looking for are available at the time + the logic is run) + +* Sat Aug 11 2012 Lennart Poettering - 188-3 +- Remount file systems MS_PRIVATE before switching roots +- https://bugzilla.redhat.com/show_bug.cgi?id=847418 + +* Wed Aug 08 2012 Rex Dieter - 188-2 +- fix scriptlets + +* Wed Aug 8 2012 Lennart Poettering - 188-1 +- New upstream release +- Enable gdm and avahi by default via the preset file +- Convert /etc/sysconfig/desktop to display-manager.service symlink +- Enable hardened build + +* Mon Jul 30 2012 Kay Sievers - 187-3 +- Obsolete: system-setup-keyboard + +* Wed Jul 25 2012 Kalev Lember - 187-2 +- Run ldconfig for the new -libs subpackage + +* Thu Jul 19 2012 Lennart Poettering - 187-1 +- New upstream release + +* Mon Jul 09 2012 Harald Hoyer 186-2 +- fixed dracut conflict version + +* Tue Jul 3 2012 Lennart Poettering - 186-1 +- New upstream release + +* Fri Jun 22 2012 Nils Philippsen - 185-7.gite7aee75 +- add obsoletes/conflicts so multilib systemd -> systemd-libs updates work + +* Thu Jun 14 2012 Michal Schmidt - 185-6.gite7aee75 +- Update to current git + +* Wed Jun 06 2012 Kay Sievers - 185-5.gita2368a3 +- disable plymouth in configure, to drop the .wants/ symlinks + +* Wed Jun 06 2012 Michal Schmidt - 185-4.gita2368a3 +- Update to current git snapshot + - Add systemd-readahead-analyze + - Drop upstream patch +- Split systemd-libs +- Drop duplicate doc files +- Fixed License headers of subpackages + +* Wed Jun 06 2012 Ray Strode - 185-3 +- Drop plymouth files +- Conflict with old plymouth + +* Tue Jun 05 2012 Kay Sievers - 185-2 +- selinux udev labeling fix +- conflict with older dracut versions for new udev file names + +* Mon Jun 04 2012 Kay Sievers - 185-1 +- New upstream release + - udev selinux labeling fixes + - new man pages + - systemctl help + +* Thu May 31 2012 Lennart Poettering - 184-1 +- New upstream release + +* Thu May 24 2012 Kay Sievers - 183-1 +- New upstream release including udev merge. + +* Wed Mar 28 2012 Michal Schmidt - 44-4 +- Add triggers from Bill Nottingham to correct the damage done by + the obsoleted systemd-units's preun scriptlet (#807457). + +* Mon Mar 26 2012 Dennis Gilmore - 44-3 +- apply patch from upstream so we can build systemd on arm and ppc +- and likely the rest of the secondary arches + +* Tue Mar 20 2012 Michal Schmidt - 44-2 +- Don't build the gtk parts anymore. They're moving into systemd-ui. +- Remove a dead patch file. + +* Fri Mar 16 2012 Lennart Poettering - 44-1 +- New upstream release +- Closes #798760, #784921, #783134, #768523, #781735 + +* Mon Feb 27 2012 Dennis Gilmore - 43-2 +- don't conflict with fedora-release systemd never actually provided +- /etc/os-release so there is no actual conflict + +* Wed Feb 15 2012 Lennart Poettering - 43-1 +- New upstream release +- Closes #789758, #790260, #790522 + +* Sat Feb 11 2012 Lennart Poettering - 42-1 +- New upstream release +- Save a bit of entropy during system installation (#789407) +- Don't own /etc/os-release anymore, leave that to fedora-release + +* Thu Feb 9 2012 Adam Williamson - 41-2 +- rebuild for fixed binutils + +* Thu Feb 9 2012 Lennart Poettering - 41-1 +- New upstream release + +* Tue Feb 7 2012 Lennart Poettering - 40-1 +- New upstream release + +* Thu Jan 26 2012 Kay Sievers - 39-3 +- provide /sbin/shutdown + +* Wed Jan 25 2012 Harald Hoyer 39-2 +- increment release + +* Wed Jan 25 2012 Kay Sievers - 39-1.1 +- install everything in /usr + https://fedoraproject.org/wiki/Features/UsrMove + +* Wed Jan 25 2012 Lennart Poettering - 39-1 +- New upstream release + +* Sun Jan 22 2012 Michal Schmidt - 38-6.git9fa2f41 +- Update to a current git snapshot. +- Resolves: #781657 + +* Sun Jan 22 2012 Michal Schmidt - 38-5 +- Build against libgee06. Reenable gtk tools. +- Delete unused patches. +- Add easy building of git snapshots. +- Remove legacy spec file elements. +- Don't mention implicit BuildRequires. +- Configure with --disable-static. +- Merge -units into the main package. +- Move section 3 manpages to -devel. +- Fix unowned directory. +- Run ldconfig in scriptlets. +- Split systemd-analyze to a subpackage. + +* Sat Jan 21 2012 Dan Horák - 38-4 +- fix build on big-endians + +* Wed Jan 11 2012 Lennart Poettering - 38-3 +- Disable building of gtk tools for now + +* Wed Jan 11 2012 Lennart Poettering - 38-2 +- Fix a few (build) dependencies + +* Wed Jan 11 2012 Lennart Poettering - 38-1 +- New upstream release + +* Tue Nov 15 2011 Michal Schmidt - 37-4 +- Run authconfig if /etc/pam.d/system-auth is not a symlink. +- Resolves: #753160 + +* Wed Nov 02 2011 Michal Schmidt - 37-3 +- Fix remote-fs-pre.target and its ordering. +- Resolves: #749940 + +* Wed Oct 19 2011 Michal Schmidt - 37-2 +- A couple of fixes from upstream: +- Fix a regression in bash-completion reported in Bodhi. +- Fix a crash in isolating. +- Resolves: #717325 + +* Tue Oct 11 2011 Lennart Poettering - 37-1 +- New upstream release +- Resolves: #744726, #718464, #713567, #713707, #736756 + +* Thu Sep 29 2011 Michal Schmidt - 36-5 +- Undo the workaround. Kay says it does not belong in systemd. +- Unresolves: #741655 + +* Thu Sep 29 2011 Michal Schmidt - 36-4 +- Workaround for the crypto-on-lvm-on-crypto disk layout +- Resolves: #741655 + +* Sun Sep 25 2011 Michal Schmidt - 36-3 +- Revert an upstream patch that caused ordering cycles +- Resolves: #741078 + +* Fri Sep 23 2011 Lennart Poettering - 36-2 +- Add /etc/timezone to ghosted files + +* Fri Sep 23 2011 Lennart Poettering - 36-1 +- New upstream release +- Resolves: #735013, #736360, #737047, #737509, #710487, #713384 + +* Thu Sep 1 2011 Lennart Poettering - 35-1 +- New upstream release +- Update post scripts +- Resolves: #726683, #713384, #698198, #722803, #727315, #729997, #733706, #734611 + +* Thu Aug 25 2011 Lennart Poettering - 34-1 +- New upstream release + +* Fri Aug 19 2011 Harald Hoyer 33-2 +- fix ABRT on service file reloading +- Resolves: rhbz#732020 + +* Wed Aug 3 2011 Lennart Poettering - 33-1 +- New upstream release + +* Fri Jul 29 2011 Lennart Poettering - 32-1 +- New upstream release + +* Wed Jul 27 2011 Lennart Poettering - 31-2 +- Fix access mode of modprobe file, restart logind after upgrade + +* Wed Jul 27 2011 Lennart Poettering - 31-1 +- New upstream release + +* Wed Jul 13 2011 Lennart Poettering - 30-1 +- New upstream release + +* Thu Jun 16 2011 Lennart Poettering - 29-1 +- New upstream release + +* Mon Jun 13 2011 Michal Schmidt - 28-4 +- Apply patches from current upstream. +- Fixes memory size detection on 32-bit with >4GB RAM (BZ712341) + +* Wed Jun 08 2011 Michal Schmidt - 28-3 +- Apply patches from current upstream +- https://bugzilla.redhat.com/show_bug.cgi?id=709909 +- https://bugzilla.redhat.com/show_bug.cgi?id=710839 +- https://bugzilla.redhat.com/show_bug.cgi?id=711015 + +* Sat May 28 2011 Lennart Poettering - 28-2 +- Pull in nss-myhostname + +* Thu May 26 2011 Lennart Poettering - 28-1 +- New upstream release + +* Wed May 25 2011 Lennart Poettering - 26-2 +- Bugfix release +- https://bugzilla.redhat.com/show_bug.cgi?id=707507 +- https://bugzilla.redhat.com/show_bug.cgi?id=707483 +- https://bugzilla.redhat.com/show_bug.cgi?id=705427 +- https://bugzilla.redhat.com/show_bug.cgi?id=707577 + +* Sat Apr 30 2011 Lennart Poettering - 26-1 +- New upstream release +- https://bugzilla.redhat.com/show_bug.cgi?id=699394 +- https://bugzilla.redhat.com/show_bug.cgi?id=698198 +- https://bugzilla.redhat.com/show_bug.cgi?id=698674 +- https://bugzilla.redhat.com/show_bug.cgi?id=699114 +- https://bugzilla.redhat.com/show_bug.cgi?id=699128 + +* Thu Apr 21 2011 Lennart Poettering - 25-1 +- New upstream release +- https://bugzilla.redhat.com/show_bug.cgi?id=694788 +- https://bugzilla.redhat.com/show_bug.cgi?id=694321 +- https://bugzilla.redhat.com/show_bug.cgi?id=690253 +- https://bugzilla.redhat.com/show_bug.cgi?id=688661 +- https://bugzilla.redhat.com/show_bug.cgi?id=682662 +- https://bugzilla.redhat.com/show_bug.cgi?id=678555 +- https://bugzilla.redhat.com/show_bug.cgi?id=628004 + +* Wed Apr 6 2011 Lennart Poettering - 24-1 +- New upstream release +- https://bugzilla.redhat.com/show_bug.cgi?id=694079 +- https://bugzilla.redhat.com/show_bug.cgi?id=693289 +- https://bugzilla.redhat.com/show_bug.cgi?id=693274 +- https://bugzilla.redhat.com/show_bug.cgi?id=693161 + +* Tue Apr 5 2011 Lennart Poettering - 23-1 +- New upstream release +- Include systemd-sysv-convert + +* Fri Apr 1 2011 Lennart Poettering - 22-1 +- New upstream release + +* Wed Mar 30 2011 Lennart Poettering - 21-2 +- The quota services are now pulled in by mount points, hence no need to enable them explicitly + +* Tue Mar 29 2011 Lennart Poettering - 21-1 +- New upstream release + +* Mon Mar 28 2011 Matthias Clasen - 20-2 +- Apply upstream patch to not send untranslated messages to plymouth + +* Tue Mar 8 2011 Lennart Poettering - 20-1 +- New upstream release + +* Tue Mar 1 2011 Lennart Poettering - 19-1 +- New upstream release + +* Wed Feb 16 2011 Lennart Poettering - 18-1 +- New upstream release + +* Mon Feb 14 2011 Bill Nottingham - 17-6 +- bump upstart obsoletes (#676815) + +* Wed Feb 9 2011 Tom Callaway - 17-5 +- add macros.systemd file for %%{_unitdir} + +* Wed Feb 09 2011 Fedora Release Engineering - 17-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Feb 9 2011 Lennart Poettering - 17-3 +- Fix popen() of systemctl, #674916 + +* Mon Feb 7 2011 Bill Nottingham - 17-2 +- add epoch to readahead obsolete + +* Sat Jan 22 2011 Lennart Poettering - 17-1 +- New upstream release + +* Tue Jan 18 2011 Lennart Poettering - 16-2 +- Drop console.conf again, since it is not shipped in pamtmp.conf + +* Sat Jan 8 2011 Lennart Poettering - 16-1 +- New upstream release + +* Thu Nov 25 2010 Lennart Poettering - 15-1 +- New upstream release + +* Thu Nov 25 2010 Lennart Poettering - 14-1 +- Upstream update +- Enable hwclock-load by default +- Obsolete readahead +- Enable /var/run and /var/lock on tmpfs + +* Fri Nov 19 2010 Lennart Poettering - 13-1 +- new upstream release + +* Wed Nov 17 2010 Bill Nottingham 12-3 +- Fix clash + +* Wed Nov 17 2010 Lennart Poettering - 12-2 +- Don't clash with initscripts for now, so that we don't break the builders + +* Wed Nov 17 2010 Lennart Poettering - 12-1 +- New upstream release + +* Fri Nov 12 2010 Matthias Clasen - 11-2 +- Rebuild with newer vala, libnotify + +* Thu Oct 7 2010 Lennart Poettering - 11-1 +- New upstream release + +* Wed Sep 29 2010 Jesse Keating - 10-6 +- Rebuilt for gcc bug 634757 + +* Thu Sep 23 2010 Bill Nottingham - 10-5 +- merge -sysvinit into main package + +* Mon Sep 20 2010 Bill Nottingham - 10-4 +- obsolete upstart-sysvinit too + +* Fri Sep 17 2010 Bill Nottingham - 10-3 +- Drop upstart requires + +* Tue Sep 14 2010 Lennart Poettering - 10-2 +- Enable audit +- https://bugzilla.redhat.com/show_bug.cgi?id=633771 + +* Tue Sep 14 2010 Lennart Poettering - 10-1 +- New upstream release +- https://bugzilla.redhat.com/show_bug.cgi?id=630401 +- https://bugzilla.redhat.com/show_bug.cgi?id=630225 +- https://bugzilla.redhat.com/show_bug.cgi?id=626966 +- https://bugzilla.redhat.com/show_bug.cgi?id=623456 + +* Fri Sep 3 2010 Bill Nottingham - 9-3 +- move fedora-specific units to initscripts; require newer version thereof + +* Fri Sep 3 2010 Lennart Poettering - 9-2 +- Add missing tarball + +* Fri Sep 3 2010 Lennart Poettering - 9-1 +- New upstream version +- Closes 501720, 614619, 621290, 626443, 626477, 627014, 627785, 628913 + +* Fri Aug 27 2010 Lennart Poettering - 8-3 +- Reexecute after installation, take ownership of /var/run/user +- https://bugzilla.redhat.com/show_bug.cgi?id=627457 +- https://bugzilla.redhat.com/show_bug.cgi?id=627634 + +* Thu Aug 26 2010 Lennart Poettering - 8-2 +- Properly create default.target link + +* Wed Aug 25 2010 Lennart Poettering - 8-1 +- New upstream release + +* Thu Aug 12 2010 Lennart Poettering - 7-3 +- Fix https://bugzilla.redhat.com/show_bug.cgi?id=623561 + +* Thu Aug 12 2010 Lennart Poettering - 7-2 +- Fix https://bugzilla.redhat.com/show_bug.cgi?id=623430 + +* Tue Aug 10 2010 Lennart Poettering - 7-1 +- New upstream release + +* Fri Aug 6 2010 Lennart Poettering - 6-2 +- properly hide output on package installation +- pull in coreutils during package installtion + +* Fri Aug 6 2010 Lennart Poettering - 6-1 +- New upstream release +- Fixes #621200 + +* Wed Aug 4 2010 Lennart Poettering - 5-2 +- Add tarball + +* Wed Aug 4 2010 Lennart Poettering - 5-1 +- Prepare release 5 + +* Tue Jul 27 2010 Bill Nottingham - 4-4 +- Add 'sysvinit-userspace' provide to -sysvinit package to fix upgrade/install (#618537) + +* Sat Jul 24 2010 Lennart Poettering - 4-3 +- Add libselinux to build dependencies + +* Sat Jul 24 2010 Lennart Poettering - 4-2 +- Use the right tarball + +* Sat Jul 24 2010 Lennart Poettering - 4-1 +- New upstream release, and make default + +* Tue Jul 13 2010 Lennart Poettering - 3-3 +- Used wrong tarball + +* Tue Jul 13 2010 Lennart Poettering - 3-2 +- Own /cgroup jointly with libcgroup, since we don't dpend on it anymore + +* Tue Jul 13 2010 Lennart Poettering - 3-1 +- New upstream release + +* Fri Jul 9 2010 Lennart Poettering - 2-0 +- New upstream release + +* Wed Jul 7 2010 Lennart Poettering - 1-0 +- First upstream release + +* Tue Jun 29 2010 Lennart Poettering - 0-0.7.20100629git4176e5 +- New snapshot +- Split off -units package where other packages can depend on without pulling in the whole of systemd + +* Tue Jun 22 2010 Lennart Poettering - 0-0.6.20100622gita3723b +- Add missing libtool dependency. + +* Tue Jun 22 2010 Lennart Poettering - 0-0.5.20100622gita3723b +- Update snapshot + +* Mon Jun 14 2010 Rahul Sundaram - 0-0.4.20100614git393024 +- Pull the latest snapshot that fixes a segfault. Resolves rhbz#603231 + +* Fri Jun 11 2010 Rahul Sundaram - 0-0.3.20100610git2f198e +- More minor fixes as per review + +* Thu Jun 10 2010 Rahul Sundaram - 0-0.2.20100610git2f198e +- Spec improvements from David Hollis + +* Wed Jun 09 2010 Rahul Sundaram - 0-0.1.20090609git2f198e +- Address review comments + +* Tue Jun 01 2010 Rahul Sundaram - 0-0.0.git2010-06-02 +- Initial spec (adopted from Kay Sievers)