diff --git a/SOURCES/0343-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch b/SOURCES/0343-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch
deleted file mode 100644
index 3e615dc..0000000
--- a/SOURCES/0343-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From 061330ee69c102b0acecec6bccacfd7080a8c378 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
-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: #1824129
----
- 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/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 <d.hatayama@fujitsu.com>
+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-core-fix-re-realization-of-cgroup-siblings.patch b/SOURCES/0344-core-fix-re-realization-of-cgroup-siblings.patch
deleted file mode 100644
index 3293ea8..0000000
--- a/SOURCES/0344-core-fix-re-realization-of-cgroup-siblings.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From 88e03493cc361628315cd4c948a6bfd01f1d3a45 Mon Sep 17 00:00:00 2001
-From: Lennart Poettering <lennart@poettering.net>
-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: #1824129
----
- 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/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 <d.hatayama@fujitsu.com>
+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-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch b/SOURCES/0345-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch
deleted file mode 100644
index 17ea6d3..0000000
--- a/SOURCES/0345-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch
+++ /dev/null
@@ -1,99 +0,0 @@
-From 77e4fe56bc4fdebb629e5250c113aa1750b0d0a2 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
-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: #1824129
----
- 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/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 <lennart@poettering.net>
+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/0346-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch b/SOURCES/0346-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch
deleted file mode 100644
index 0c8e0e7..0000000
--- a/SOURCES/0346-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch
+++ /dev/null
@@ -1,80 +0,0 @@
-From 0c0129bd214df25318d267fbbfe2cde6fc18b465 Mon Sep 17 00:00:00 2001
-From: HATAYAMA Daisuke <d.hatayama@fujitsu.com>
-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)
-(cherry picked from commit b89a1a9d19aa806feb984c8dba25116b5b5a52bc)
-
-Resolves: #1821372
----
- 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/0347-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch b/SOURCES/0347-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch
deleted file mode 100644
index b1240c9..0000000
--- a/SOURCES/0347-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch
+++ /dev/null
@@ -1,62 +0,0 @@
-From fab8f70326d687479ef63fe177572ab512bf965e Mon Sep 17 00:00:00 2001
-From: Lennart Poettering <lennart@poettering.net>
-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: #1827621
----
- 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 @@
-         </listitem>
-       </varlistentry>
- 
-+      <varlistentry>
-+        <term><varname>systemd.cpu_affinity=</varname></term>
-+
-+        <listitem>
-+          <para>Overrides the CPU affinity mask for the service manager and the default for all child
-+          processes it forks. This takes precedence over <varname>CPUAffinity=</varname>, see
-+          <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-+          for details.</para>
-+        </listitem>
-+      </varlistentry>
-+
-       <varlistentry>
-         <term><varname>modules_load=</varname></term>
-         <term><varname>rd.modules_load=</varname></term>
-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/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?= <zbyszek@in.waw.pl>
+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 <lennart@poettering.net>
+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/0348-sd-journal-close-journal-files-that-were-deleted-by-.patch b/SOURCES/0348-sd-journal-close-journal-files-that-were-deleted-by-.patch
deleted file mode 100644
index 9cd8649..0000000
--- a/SOURCES/0348-sd-journal-close-journal-files-that-were-deleted-by-.patch
+++ /dev/null
@@ -1,75 +0,0 @@
-From c01a24e9ad46eb17de5c37296194d2ce97916439 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
-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: #1826217
----
- 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/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?= <msekleta@redhat.com>
+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/0349-sd-journal-remove-the-dead-code-and-actually-fix-146.patch b/SOURCES/0349-sd-journal-remove-the-dead-code-and-actually-fix-146.patch
deleted file mode 100644
index 6b07582..0000000
--- a/SOURCES/0349-sd-journal-remove-the-dead-code-and-actually-fix-146.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 0faa7696c9fa504b7cfea189340871d262814c40 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
-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: #1826217
----
- 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/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 <the.anitazha@gmail.com>
+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 <<EOF
++[Unit]
++Description=Testsuite service
++
++[Service]
++ExecStart=/testsuite.sh
++Type=oneshot
++EOF
++        cat > $initdir/etc/systemd/system/issue_14566_test.service << EOF
++[Unit]
++Description=Issue 14566 Repro
++
++[Service]
++ExecStart=/repro.sh
++ExecStopPost=/bin/true
++KillMode=mixed
++EOF
++
++        cp testsuite.sh $initdir/
++        cp repro.sh $initdir/
++
++        setup_testsuite
++    )
++    setup_nspawn_root
++
++    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/0350-test-do-not-use-global-variable-to-pass-error.patch b/SOURCES/0350-test-do-not-use-global-variable-to-pass-error.patch
deleted file mode 100644
index 23b3734..0000000
--- a/SOURCES/0350-test-do-not-use-global-variable-to-pass-error.patch
+++ /dev/null
@@ -1,59 +0,0 @@
-From 31cb1d714effd3f7fae4f079d5639e74b3349a17 Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit a521c49bd30f6b985bb43284d7162b7b68ec7d1c)
-
-Resolves: #1836979
----
- 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/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?= <msekleta@redhat.com>
+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/0351-test-install-libraries-required-by-tests.patch b/SOURCES/0351-test-install-libraries-required-by-tests.patch
deleted file mode 100644
index 3962944..0000000
--- a/SOURCES/0351-test-install-libraries-required-by-tests.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From ace270db3fcf163ed2c09a67236c1b4d55a0a4e7 Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-Date: Wed, 12 Sep 2018 18:19:45 +0900
-Subject: [PATCH] test: install libraries required by tests
-
-(cherry picked from commit e3d3dada248c5f30e2978840ca1f0a03a4675b53)
-(cherry picked from commit 813d60bd9d668eb1e5c897edbea966bfae25647c)
-
-Resolves: #1836979
----
- 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/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?= <msekleta@redhat.com>
+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/0352-test-introduce-install_zoneinfo.patch b/SOURCES/0352-test-introduce-install_zoneinfo.patch
deleted file mode 100644
index 3676038..0000000
--- a/SOURCES/0352-test-introduce-install_zoneinfo.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From fcea985d245dd5854aee2bad8b84b0ace0c93fec Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit a66d62e7c8c28d9e5382362a279d1dfa06089996)
-
-Resolves: #1836979
----
- 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/0353-test-replace-duplicated-Makefile-by-symbolic-link.patch b/SOURCES/0353-test-replace-duplicated-Makefile-by-symbolic-link.patch
deleted file mode 100644
index 2997332..0000000
--- a/SOURCES/0353-test-replace-duplicated-Makefile-by-symbolic-link.patch
+++ /dev/null
@@ -1,152 +0,0 @@
-From d87f4abdf99b0bdfeec4eaf05a660dd7d7291a0b Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-Date: Thu, 13 Sep 2018 03:01:42 +0900
-Subject: [PATCH] test: replace duplicated Makefile by symbolic link
-
-(cherry picked from commit dd75c133d81f07c56c82ee4e7a80f391ffebd9ce)
-(cherry picked from commit e02d918ddcc77b9ad87a328482b7a2c6f95d31ce)
-
-Resolves: #1836979
----
- 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/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?= <zbyszek@in.waw.pl>
+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 <lennart@poettering.net>
+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 @@
+                 </defaults>
+         </action>
+ 
++        <action id="org.freedesktop.login1.chvt">
++                <description gettext-domain="systemd">Change Session</description>
++                <message gettext-domain="systemd">Authentication is required for changing the virtual terminal.</message>
++                <defaults>
++                        <allow_any>auth_admin_keep</allow_any>
++                        <allow_inactive>auth_admin_keep</allow_inactive>
++                        <allow_active>yes</allow_active>
++                </defaults>
++        </action>
++
+ </policyconfig>
diff --git a/SOURCES/0354-test-add-paths-of-keymaps-in-install_keymaps.patch b/SOURCES/0354-test-add-paths-of-keymaps-in-install_keymaps.patch
deleted file mode 100644
index acadc38..0000000
--- a/SOURCES/0354-test-add-paths-of-keymaps-in-install_keymaps.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From d3ceee6505786b406da833b50e3d537da607612d Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit 808bf3d4c789019fd7cc1a0568f3836e200d9550)
-
-Resolves: #1836979
----
- 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/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 <watanabe.yu+github@gmail.com>
+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/0355-test-make-install_keymaps-optionally-install-more-ke.patch b/SOURCES/0355-test-make-install_keymaps-optionally-install-more-ke.patch
deleted file mode 100644
index 24e5150..0000000
--- a/SOURCES/0355-test-make-install_keymaps-optionally-install-more-ke.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 7e0f8e0f9c6790f2c2954d2102a478953e7a74af Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-Date: Wed, 19 Sep 2018 10:54:28 +0900
-Subject: [PATCH] test: make install_keymaps() optionally install more keymaps
-
-(cherry picked from commit ad931fee506e1313e8a520ae0ecc1c8e275d9941)
-(cherry picked from commit bc06966ff3d335102287a4d0ec525e527bc6a139)
-
-Resolves: #1836979
----
- 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/0356-test-fs-util-skip-some-tests-when-running-in-unprivi.patch b/SOURCES/0356-test-fs-util-skip-some-tests-when-running-in-unprivi.patch
deleted file mode 100644
index 54abfcb..0000000
--- a/SOURCES/0356-test-fs-util-skip-some-tests-when-running-in-unprivi.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 5db1bccba4a0b5970462e06a1274fcc6fd34bdcc Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit bf2234c276fd14ac78e2c559c762751ea6145441)
-
-Resolves: #1836979
----
- 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/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 <watanabe.yu+github@gmail.com>
+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 <watanabe.yu+github@gmail.com>
+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/0357-test-process-util-skip-several-verifications-when-ru.patch b/SOURCES/0357-test-process-util-skip-several-verifications-when-ru.patch
deleted file mode 100644
index 12b9132..0000000
--- a/SOURCES/0357-test-process-util-skip-several-verifications-when-ru.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 6f3d631982c7914f63f1e5e8eb171a1646105395 Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit 2bc26a0ac181124ba06022bcf4744ae7d5bdcc9d)
-
-Resolves: #1836979
----
- 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/0358-test-execute-also-check-python3-is-installed-or-not.patch b/SOURCES/0358-test-execute-also-check-python3-is-installed-or-not.patch
deleted file mode 100644
index 29ee223..0000000
--- a/SOURCES/0358-test-execute-also-check-python3-is-installed-or-not.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 464a88eeb63c0e733633c7d8eef3a48f68b8440e Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit 6750925014bcd2d2289af08cdc5d81b673c36779)
-
-Resolves: #1836979
----
- 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/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 <watanabe.yu+github@gmail.com>
+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 <watanabe.yu+github@gmail.com>
+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/0359-test-execute-skip-several-tests-when-running-in-cont.patch b/SOURCES/0359-test-execute-skip-several-tests-when-running-in-cont.patch
deleted file mode 100644
index 4340dc9..0000000
--- a/SOURCES/0359-test-execute-skip-several-tests-when-running-in-cont.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 127c5a52752f34ac6dbf2e818f036308d94ea81f Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-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)
-(cherry picked from commit 3a8ebd888d97c9f83df724264b47475e315d1a20)
-
-Resolves: #1836979
----
- 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/0360-test-introduce-test_is_running_from_builddir.patch b/SOURCES/0360-test-introduce-test_is_running_from_builddir.patch
deleted file mode 100644
index 973e0f8..0000000
--- a/SOURCES/0360-test-introduce-test_is_running_from_builddir.patch
+++ /dev/null
@@ -1,71 +0,0 @@
-From b97351958572028b6c23945bbf2d8f509f2e29a2 Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-Date: Tue, 11 Sep 2018 09:17:22 +0900
-Subject: [PATCH] test: introduce test_is_running_from_builddir()
-
-(cherry picked from commit 8cb10a4f4dabc508a04f76ea55f23ef517881b61)
-(cherry picked from commit 4fa54d7a72600788349fc363b7814bb52849b5e6)
-
-Resolves: #1836979
----
- 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/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 <watanabe.yu+github@gmail.com>
+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 <watanabe.yu+github@gmail.com>
+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/0361-test-make-test-catalog-relocatable.patch b/SOURCES/0361-test-make-test-catalog-relocatable.patch
deleted file mode 100644
index 3b1cf88..0000000
--- a/SOURCES/0361-test-make-test-catalog-relocatable.patch
+++ /dev/null
@@ -1,104 +0,0 @@
-From 4b54464394c7cddf7e8335d976632bae1240846a Mon Sep 17 00:00:00 2001
-From: Yu Watanabe <watanabe.yu+github@gmail.com>
-Date: Tue, 11 Sep 2018 09:18:33 +0900
-Subject: [PATCH] test: make test-catalog relocatable
-
-Fixes #10045.
-
-(cherry picked from commit d9b6baa69968132d33e4ad8627c7fe0bd527c859)
-(cherry picked from commit 444ad721dc163d4952bb35a0b061e57c91bfc9ff)
-
-Resolves: #1836979
----
- 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/0362-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch b/SOURCES/0362-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch
deleted file mode 100644
index 4deb641..0000000
--- a/SOURCES/0362-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch
+++ /dev/null
@@ -1,138 +0,0 @@
-From 0b08d53cb68f008610722773ee4e9188b8e67d06 Mon Sep 17 00:00:00 2001
-From: Frantisek Sumsal <frantisek@sumsal.cz>
-Date: Tue, 5 Mar 2019 13:50:28 +0100
-Subject: [PATCH] test: parallelize tasks in TEST-24-UNIT-TESTS
-
-(cherry picked from commit 2f2a0454efd07644a4e0ccb3f00f1db2d7043391)
-(cherry picked from commit c06e3ad98d2d6bc03d3ed2cdeb1e73283702ef3a)
-
-Resolves: #1836979
----
- 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 <<EOF
-diff --git a/test/TEST-24-UNIT-TESTS/testsuite.sh b/test/TEST-24-UNIT-TESTS/testsuite.sh
-index 7c0e495dbd..7c7a068556 100755
---- a/test/TEST-24-UNIT-TESTS/testsuite.sh
-+++ b/test/TEST-24-UNIT-TESTS/testsuite.sh
-@@ -4,31 +4,84 @@
- #set -ex
- #set -o pipefail
- 
--for i in /usr/lib/systemd/tests/test-*; do
--    if [[ ! -x $i ]]; then continue; fi
--    NAME=${i##*/}
--    echo "Running $NAME"
--    $i > /$NAME.log 2>&1
--    ret=$?
--    if (( $ret && $ret != 77 )); then
--        echo "$NAME failed with $ret"
--        echo $NAME >> /failed-tests
--        echo "--- $NAME begin ---" >> /failed
--        cat /$NAME.log >> /failed
--        echo "--- $NAME end ---" >> /failed
--    elif (( $ret == 77 )); then
--        echo "$NAME skipped"
--        echo $NAME >> /skipped-tests
--        echo "--- $NAME begin ---" >> /skipped
--        cat /$NAME.log >> /skipped
--        echo "--- $NAME end ---" >> /skipped
-+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/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 <watanabe.yu+github@gmail.com>
+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 <watanabe.yu+github@gmail.com>
+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/0363-test-try-to-determine-QEMU_SMP-dynamically.patch b/SOURCES/0363-test-try-to-determine-QEMU_SMP-dynamically.patch
deleted file mode 100644
index 714e6ec..0000000
--- a/SOURCES/0363-test-try-to-determine-QEMU_SMP-dynamically.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From c83025b14c1ec1aead2e6940e023c098ccc8fa9c Mon Sep 17 00:00:00 2001
-From: Frantisek Sumsal <frantisek@sumsal.cz>
-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)
-(cherry picked from commit dd166ab28360ceeb821d7b6d04e3c360b9023896)
-
-Resolves: #1836979
----
- 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/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 <watanabe.yu+github@gmail.com>
+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/0364-test-store-coredumps-in-journal.patch b/SOURCES/0364-test-store-coredumps-in-journal.patch
deleted file mode 100644
index f9c3db9..0000000
--- a/SOURCES/0364-test-store-coredumps-in-journal.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From c8e3d9e23d99f2f0f01bd5f367f6bce37d2f04b9 Mon Sep 17 00:00:00 2001
-From: Frantisek Sumsal <frantisek@sumsal.cz>
-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)
-(cherry picked from commit cac1e15554b8e3f102e00ac802cff7100b1def22)
-
-Resolves: #1836979
----
- 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/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 <watanabe.yu+github@gmail.com>
+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/0365-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch b/SOURCES/0365-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch
deleted file mode 100644
index 0604522..0000000
--- a/SOURCES/0365-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch
+++ /dev/null
@@ -1,57 +0,0 @@
-From a4035d7ebcb2bbc173a626f69798bedba03978b9 Mon Sep 17 00:00:00 2001
-From: Joerg Steffens <joerg.steffens@bareos.com>
-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)
-(cherry picked from commit f60e89ea4c38c11a9d0c1e642c0a78faa32aca56)
-Resolves: #1836981
----
- 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/0366-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch b/SOURCES/0366-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch
deleted file mode 100644
index fdb6bbf..0000000
--- a/SOURCES/0366-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 63e2cd09f2c266d9a67937f2d1c13d518c988312 Mon Sep 17 00:00:00 2001
-From: Lennart Poettering <lennart@poettering.net>
-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)
-(cherry picked from commit 8ee1465520ad49892a0a378626ef93abc03f4d4e)
-
-Resolves: #1837423
----
- 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/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 <watanabe.yu+github@gmail.com>
+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-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch b/SOURCES/0367-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch
deleted file mode 100644
index eab0e93..0000000
--- a/SOURCES/0367-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 9b40608e772dac4e0fa7b5be3a283360efb282e7 Mon Sep 17 00:00:00 2001
-From: Evgeny Vereshchagin <evvers@ya.ru>
-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)
-(cherry picked from commit f4a34d97bd7e1564a51f590df591cb31a1a3f333)
-
-Resolves: #1837423
----
- 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/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 <frantisek@sumsal.cz>
+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 <<EOF
+diff --git a/test/TEST-24-UNIT-TESTS/testsuite.sh b/test/TEST-24-UNIT-TESTS/testsuite.sh
+index 7c0e495dbd..7c7a068556 100755
+--- a/test/TEST-24-UNIT-TESTS/testsuite.sh
++++ b/test/TEST-24-UNIT-TESTS/testsuite.sh
+@@ -4,31 +4,84 @@
+ #set -ex
+ #set -o pipefail
+ 
+-for i in /usr/lib/systemd/tests/test-*; do
+-    if [[ ! -x $i ]]; then continue; fi
+-    NAME=${i##*/}
+-    echo "Running $NAME"
+-    $i > /$NAME.log 2>&1
+-    ret=$?
+-    if (( $ret && $ret != 77 )); then
+-        echo "$NAME failed with $ret"
+-        echo $NAME >> /failed-tests
+-        echo "--- $NAME begin ---" >> /failed
+-        cat /$NAME.log >> /failed
+-        echo "--- $NAME end ---" >> /failed
+-    elif (( $ret == 77 )); then
+-        echo "$NAME skipped"
+-        echo $NAME >> /skipped-tests
+-        echo "--- $NAME begin ---" >> /skipped
+-        cat /$NAME.log >> /skipped
+-        echo "--- $NAME end ---" >> /skipped
++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 <frantisek@sumsal.cz>
+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 <frantisek@sumsal.cz>
+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 <lennart@poettering.net>
+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 @@
+         </listitem>
+       </varlistentry>
+ 
++      <varlistentry>
++        <term><varname>systemd.cpu_affinity=</varname></term>
++
++        <listitem>
++          <para>Overrides the CPU affinity mask for the service manager and the default for all child
++          processes it forks. This takes precedence over <varname>CPUAffinity=</varname>, see
++          <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
++          for details.</para>
++        </listitem>
++      </varlistentry>
++
+       <varlistentry>
+         <term><varname>modules_load=</varname></term>
+         <term><varname>rd.modules_load=</varname></term>
+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 <joerg.steffens@bareos.com>
+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 <lennart@poettering.net>
+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.</para>
+ 
+-        <para>Multiple directives may be combined of the same and of
+-        different types. For example, by combining
+-        <varname>OnBootSec=</varname> and
+-        <varname>OnUnitActiveSec=</varname>, it is possible to define
+-        a timer that elapses in regular intervals and activates a
+-        specific service each time.</para>
++        <para>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
++        <varname>OnBootSec=</varname> and <varname>OnUnitActiveSec=</varname>, 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 <varname>OnCalendar=</varname> calendar expressions may be combined in
++        the same timer unit.</para>
+ 
+         <para>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.</para>
+ 
+-        <para>These are monotonic timers, independent of wall-clock
+-        time and timezones. If the computer is temporarily suspended,
+-        the monotonic clock stops too.</para>
++        <para>These are monotonic timers, independent of wall-clock time and timezones. If the computer is
++        temporarily suspended, the monotonic clock pauses, too.</para>
+ 
+-        <para>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.</para>
++        <para>If the empty string is assigned to any of these options, the list of timers is reset (both
++        monotonic timers and <varname>OnCalendar=</varname> timers, see below), and all prior assignments
++        will have no effect.</para>
+ 
+         <para>Note that timers do not necessarily expire at the
+         precise time configured with these settings, as they are
+@@ -175,7 +174,13 @@
+         the <varname>AccuracySec=</varname> setting
+         below.</para>
+ 
+-       <para>May be specified more than once.</para></listitem>
++        <para>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.</para>
++
++        <para>If the empty string is assigned to any of these options, the list of timers is reset (both
++        <varname>OnCalendar=</varname> timers and monotonic timers, see above), and all prior assignments
++        will have no effect.</para></listitem>
+       </varlistentry>
+ 
+       <varlistentry>
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 <zsol.zsol@gmail.com>
+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</programlisting>
+         of <option>inherit</option>, <option>null</option>, <option>tty</option>, <option>journal</option>,
+         <option>syslog</option>, <option>kmsg</option>, <option>journal+console</option>,
+         <option>syslog+console</option>, <option>kmsg+console</option>,
+-        <option>file:<replaceable>path</replaceable></option>, <option>socket</option> or
+-        <option>fd:<replaceable>name</replaceable></option>.</para>
++        <option>file:<replaceable>path</replaceable></option>, <option>append:<replaceable>path</replaceable></option>,
++        <option>socket</option> or<option>fd:<replaceable>name</replaceable></option>.</para>
+ 
+         <para><option>inherit</option> duplicates the file descriptor of standard input for standard output.</para>
+ 
+@@ -1824,11 +1824,17 @@ SystemCallErrorNumber=EPERM</programlisting>
+ 
+         <para>The <option>file:<replaceable>path</replaceable></option> option may be used to connect a specific file
+         system object to standard output. The semantics are similar to the same option of
+-        <varname>StandardInput=</varname>, see above. If standard input and output are directed to the same file path,
+-        it is opened only once, for reading as well as writing and duplicated. This is particular useful when the
+-        specified path refers to an <constant>AF_UNIX</constant> socket in the file system, as in that case only a
++        <varname>StandardInput=</varname>, see above. If <replaceable>path</replaceable> refers to a regular file
++        on the filesystem, it is opened (created if it doesn't exist yet) for writing at the beginning of the file,
++        but without truncating it.
++        If standard input and output are directed to the same file path, it is opened only once, for reading as well
++        as writing and duplicated. This is particularly useful when the specified path refers to an
++        <constant>AF_UNIX</constant> socket in the file system, as in that case only a
+         single stream connection is created for both input and output.</para>
+ 
++        <para><option>append:<replaceable>path</replaceable></option> is similar to <option>file:<replaceable>path
++        </replaceable></option> above, but it opens the file in append mode.</para>
++
+         <para><option>socket</option> connects standard output to a socket acquired via socket activation. The
+         semantics are similar to the same option of <varname>StandardInput=</varname>, see above.</para>
+ 
+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 <lennart@poettering.net>
+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 <evvers@ya.ru>
+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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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?= <zbyszek@in.waw.pl>
+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?= <zbyszek@in.waw.pl>
+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 <the.anitazha@gmail.com>
+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 @@
+         </listitem>
+       </varlistentry>
+ 
++      <varlistentry>
++        <term><varname>ExecCondition=</varname></term>
++        <listitem><para>Optional commands that are executed before the command(s) in <varname>ExecStartPre=</varname>.
++        Syntax is the same as for <varname>ExecStart=</varname>, except that multiple command lines are allowed and the
++        commands are executed one after the other, serially.</para>
++
++        <para>The behavior is like an <varname>ExecStartPre=</varname> and condition check hybrid: when an
++        <varname>ExecCondition=</varname> command exits with exit code 1 through 254 (inclusive), the remaining
++        commands are skipped and the unit is <emphasis>not</emphasis> marked as failed. However, if an
++        <varname>ExecCondition=</varname> 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 <varname>SuccessExitStatus=</varname> will continue execution to the next command(s).</para>
++
++        <para>The same recommendations about not running long-running processes in <varname>ExecStartPre=</varname>
++        also applies to <varname>ExecCondition=</varname>. <varname>ExecCondition=</varname> will also run the commands
++        in <varname>ExecStopPost=</varname>, as part of stopping the service, in the case of any non-zero or abnormal
++        exits, like the ones described above.</para>
++        </listitem>
++      </varlistentry>
++
+       <varlistentry>
+         <term><varname>ExecReload=</varname></term>
+         <listitem><para>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?= <zbyszek@in.waw.pl>
+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?= <zbyszek@in.waw.pl>
+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?= <zbyszek@in.waw.pl>
+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?= <zbyszek@in.waw.pl>
+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 <errno.h>
++
++#include "alloc-util.h"
++#include "compress.h"
++#include "fuzz.h"
++
++static int compress(int alg,
++                    const void *src, uint64_t src_size,
++                    void *dst, size_t dst_alloc_size, size_t *dst_size) {
++
++        if (alg == OBJECT_COMPRESSED_LZ4)
++                return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
++        if (alg == OBJECT_COMPRESSED_XZ)
++                return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
++        return -EOPNOTSUPP;
++}
++
++typedef struct header {
++        uint32_t alg:2; /* We have only two compression algorithms so far, but we might add
++                         * more in the future. Let's make this a bit wider so our fuzzer
++                         * cases remain stable in the future. */
++        uint32_t sw_len;
++        uint32_t sw_alloc;
++        uint32_t reserved[3]; /* Extra space to keep fuzz cases stable in case we need to
++                               * add stuff in the future. */
++        uint8_t data[];
++} header;
++
++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
++        _cleanup_free_ void *buf = NULL, *buf2 = NULL;
++        int r;
++
++        if (size < offsetof(header, data) + 1)
++                return 0;
++
++        const header *h = (struct header*) data;
++        const size_t data_len = size - offsetof(header, data);
++
++        int alg = h->alg;
++
++        /* We don't want to fill the logs with messages about parse errors.
++         * Disable most logging if not running standalone */
++        if (!getenv("SYSTEMD_LOG_LEVEL"))
++                log_set_max_level(LOG_CRIT);
++
++        log_info("Using compression %s, data size=%zu",
++                 object_compressed_to_string(alg) ?: "(none)",
++                 data_len);
++
++        buf = malloc(MAX(size, 128u)); /* Make the buffer a bit larger for very small data */
++        if (!buf) {
++                log_oom();
++                return 0;
++        }
++
++        size_t csize;
++        r = compress(alg, h->data, data_len, buf, size, &csize);
++        if (r < 0) {
++                log_error_errno(r, "Compression failed: %m");
++                return 0;
++        }
++
++        log_debug("Compressed %zu bytes to → %zu bytes", data_len, csize);
++
++        size_t sw_alloc = MAX(h->sw_alloc, 1u);
++        buf2 = malloc(sw_alloc);
++        if (!buf) {
++                log_oom();
++                return 0;
++        }
++
++        size_t sw_len = MIN(data_len - 1, h->sw_len);
++
++        r = decompress_startswith(alg, buf, csize, &buf2, &sw_alloc, h->data, sw_len, h->data[sw_len]);
++        assert_se(r > 0);
++
++        return 0;
++}
+diff --git a/src/fuzz/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 <jsynacek@redhat.com>
+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?= <zbyszek@in.waw.pl>
+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 <andreas@fatal.se>
+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 <filbranden@google.com>
+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 </etc/resolv.conf
+  Unknown interface 'nonexistent': No such device
+
+Fixes #9423.
+
+(cherry picked from commit 5a01b3f35d7b6182c78b6973db8d99bdabd4f9c3)
+
+Resolves: #1835594
+---
+ src/resolve/resolvconf-compat.c | 4 +++-
+ src/resolve/resolvectl.c        | 2 +-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/resolve/resolvconf-compat.c b/src/resolve/resolvconf-compat.c
+index d7e68003e6..0723458945 100644
+--- a/src/resolve/resolvconf-compat.c
++++ b/src/resolve/resolvconf-compat.c
+@@ -53,6 +53,8 @@ static int parse_nameserver(const char *string) {
+ 
+                 if (strv_push(&arg_set_dns, word) < 0)
+                         return log_oom();
++
++                word = NULL;
+         }
+ 
+         return 0;
+@@ -202,7 +204,7 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
+ 
+         dot = strchr(argv[optind], '.');
+         if (dot) {
+-                iface = strndupa(argv[optind], dot - argv[optind]);
++                iface = strndup(argv[optind], dot - argv[optind]);
+                 log_debug("Ignoring protocol specifier '%s'.", dot + 1);
+         } else
+                 iface = argv[optind];
+diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c
+index cf1ec323a4..355e5c62b1 100644
+--- a/src/resolve/resolvectl.c
++++ b/src/resolve/resolvectl.c
+@@ -3090,7 +3090,7 @@ int main(int argc, char **argv) {
+                 goto finish;
+         }
+ 
+-        if (streq(program_invocation_short_name, "systemd-resolve"))
++        if (STR_IN_SET(program_invocation_short_name, "systemd-resolve", "resolvconf"))
+                 r = compat_main(argc, argv, bus);
+         else
+                 r = native_main(argc, argv, bus);
diff --git a/SOURCES/0393-mount-don-t-add-Requires-for-tmp.mount.patch b/SOURCES/0393-mount-don-t-add-Requires-for-tmp.mount.patch
new file mode 100644
index 0000000..aa7f882
--- /dev/null
+++ b/SOURCES/0393-mount-don-t-add-Requires-for-tmp.mount.patch
@@ -0,0 +1,26 @@
+From 56bc690688957abe24d434b7ede16cbe0f2d1233 Mon Sep 17 00:00:00 2001
+From: David Tardon <dtardon@redhat.com>
+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 <ypf791@gmail.com>
+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 <tj@kernel.org>
+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 @@
+         <listitem>
+           <para>Set the per-device overall block I/O weight for the executed processes, if the unified control group
+           hierarchy is used on the system. Takes a space-separated pair of a file path and a weight value to specify
+-          the device specific weight value, between 1 and 10000. (Example: "/dev/sda 1000"). The file path may be
+-          specified as path to a block device node or as any other file, in which case the backing block device of the
+-          file system of the file is determined. This controls the <literal>io.weight</literal> control group
+-          attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices. For
+-          details about this control group attribute, see <ulink
++          the device specific weight value, between 1 and 10000. (Example: <literal>/dev/sda 1000</literal>). The file
++          path may be specified as path to a block device node or as any other file, in which case the backing block
++          device of the file system of the file is determined. This controls the <literal>io.weight</literal> control
++          group attribute, which defaults to 100. Use this option multiple times to set weights for multiple devices.
++          For details about this control group attribute, see <ulink
+           url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ 
+           <para>Implies <literal>IOAccounting=true</literal>.</para>
+@@ -482,6 +482,25 @@
+         </listitem>
+       </varlistentry>
+ 
++      <varlistentry>
++        <term><varname>IODeviceLatencyTargetSec=<replaceable>device</replaceable> <replaceable>target</replaceable></varname></term>
++
++        <listitem>
++          <para>Set the per-device average target I/O latency for the executed processes, if the unified control group
++          hierarchy is used on the system. Takes a file path and a timespan separated by a space to specify
++          the device specific latency target. (Example: "/dev/sda 25ms"). The file path may be specified
++          as path to a block device node or as any other file, in which case the backing block device of the file
++          system of the file is determined. This controls the <literal>io.latency</literal> control group
++          attribute. Use this option multiple times to set latency target for multiple devices. For details about this
++          control group attribute, see <ulink
++          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
++
++          <para>Implies <literal>IOAccounting=true</literal>.</para>
++
++          <para>These settings are supported only if the unified control group hierarchy is used.</para>
++        </listitem>
++      </varlistentry>
++
+       <varlistentry>
+         <term><varname>IPAccounting=</varname></term>
+ 
+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 <filbranden@google.com>
+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 <lennart@poettering.net>
+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 <filbranden@google.com>
+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 @@
+         </listitem>
+       </varlistentry>
+ 
++      <varlistentry>
++        <term><varname>CPUQuotaPeriodSec=</varname></term>
++
++        <listitem>
++          <para>Assign the duration over which the CPU time quota specified by <varname>CPUQuota=</varname> 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 <varname>CPUQuotaPeriodSec=</varname> to an empty value resets it to the default.</para>
++
++          <para>This controls the second field of <literal>cpu.max</literal> attribute on the unified control group hierarchy
++          and <literal>cpu.cfs_period_us</literal> on legacy. For details about these control group attributes, see
++          <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink> and
++          <ulink url="https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para>
++
++          <para>Example: <varname>CPUQuotaPeriodSec=10ms</varname> to request that the CPU quota is measured in periods of 10ms.</para>
++        </listitem>
++      </varlistentry>
++
+       <varlistentry>
+         <term><varname>MemoryAccounting=</varname></term>
+ 
+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 <filbranden@google.com>
+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 <david.rheinsberg@gmail.com>
+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 <david.rheinsberg@gmail.com>
+(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 <david.rheinsberg@gmail.com>
+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 <protocol>" 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 <protocol> <hex-data>" 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 <david.rheinsberg@gmail.com>
+(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 <david.rheinsberg@gmail.com>
+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 <uid>
+    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 <server-id>
+    AGREE_UNIX_FD                       (optional)
+
+to:
+
+    DATA
+    OK <server-id>
+    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 <server-id>\r\n" replies instead of "DATA\r\n" replies.
+
+Signed-off-by: David Rheinsberg <david.rheinsberg@gmail.com>
+(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 <server-id>\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 <id>" to
++                 * "AUTH" requests from a client, even if the "AUTH" line did not contain inlined
++                 * arguments. Therefore, we also accept "OK <id>" here, even though it is technically the
++                 * wrong reply. We ignore the "<id>" 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 <tj@kernel.org>
+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 @@
+         </listitem>
+       </varlistentry>
+ 
++      <varlistentry>
++        <term><varname>MemoryMin=<replaceable>bytes</replaceable></varname></term>
++
++        <listitem>
++          <para>Specify the memory usage protection of the executed processes in this unit. If the memory usages of
++          this unit and all its ancestors are below their minimum boundaries, this unit's memory won't be reclaimed.</para>
++
++          <para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
++          parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
++          percentage value may be specified, which is taken relative to the installed physical memory on the
++          system. This controls the <literal>memory.min</literal> control group attribute. For details about this
++          control group attribute, see <ulink
++          url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
++
++          <para>Implies <literal>MemoryAccounting=true</literal>.</para>
++
++          <para>This setting is supported only if the unified control group hierarchy is used and disables
++          <varname>MemoryLimit=</varname>.</para>
++        </listitem>
++      </varlistentry>
++
+       <varlistentry>
+         <term><varname>MemoryLow=<replaceable>bytes</replaceable></varname></term>
+ 
+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 <watanabe.yu+github@gmail.com>
+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 <filbranden@google.com>
+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 <sys/stat.h>
+ #include <fcntl.h>
+ 
++#include "alloc-util.h"
+ #include "log.h"
+ #include "journal-importer.h"
++#include "path-util.h"
+ #include "string-util.h"
+ #include "tests.h"
+ 
+@@ -20,9 +22,11 @@ static void assert_iovec_entry(const struct iovec *iovec, const char* content) {
+ 
+ static void test_basic_parsing(void) {
+         _cleanup_(journal_importer_cleanup) JournalImporter imp = {};
++        _cleanup_free_ char *journal_data_path = NULL;
+         int r;
+ 
+-        imp.fd = open(get_testdata_dir("/journal-data/journal-1.txt"), O_RDONLY|O_CLOEXEC);
++        journal_data_path = path_join(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 <chris@chrisdown.name>
+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 @@
+ 
+           <para>This setting is supported only if the unified control group hierarchy is used and disables
+           <varname>MemoryLimit=</varname>.</para>
++
++          <para>Units may can have their children use a default <literal>memory.low</literal> value by specifying
++          <varname>DefaultMemoryLow=</varname>, which has the same usage as <varname>MemoryLow=</varname>. This setting
++          does not affect <literal>memory.low</literal> in the unit itself.</para>
+         </listitem>
+       </varlistentry>
+ 
+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 <stdio.h>
++
++#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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 @@
+ 
+           <para>This setting is supported only if the unified control group hierarchy is used and disables
+           <varname>MemoryLimit=</varname>.</para>
++
++          <para>Units may have their children use a default <literal>memory.min</literal> value by specifying
++          <varname>DefaultMemoryMin=</varname>, which has the same semantics as <varname>MemoryMin=</varname>. This setting
++          does not affect <literal>memory.min</literal> in the unit itself.</para>
+         </listitem>
+       </varlistentry>
+ 
+@@ -306,8 +310,8 @@
+           <para>This setting is supported only if the unified control group hierarchy is used and disables
+           <varname>MemoryLimit=</varname>.</para>
+ 
+-          <para>Units may can have their children use a default <literal>memory.low</literal> value by specifying
+-          <varname>DefaultMemoryLow=</varname>, which has the same usage as <varname>MemoryLow=</varname>. This setting
++          <para>Units may have their children use a default <literal>memory.low</literal> value by specifying
++          <varname>DefaultMemoryLow=</varname>, which has the same semantics as <varname>MemoryLow=</varname>. This setting
+           does not affect <literal>memory.low</literal> in the unit itself.</para>
+         </listitem>
+       </varlistentry>
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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 <chris@chrisdown.name>
+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 <jsynacek@redhat.com>
+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 <the.anitazha@gmail.com>
+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 <<EOF
++[Unit]
++Description=Testsuite service
++
++[Service]
++ExecStart=/testsuite.sh
++Type=oneshot
++EOF
++
++        cp testsuite.sh $initdir/
++        cp repro-?.service $initdir/etc/systemd/system/
++
++        setup_testsuite
++    )
++    setup_nspawn_root
++
++    ddebug "umount $TESTDIR/root"
++    umount $TESTDIR/root
++}
++
++do_test "$@"
+diff --git a/test/TEST-51-ISSUE-16115/testsuite.sh b/test/TEST-51-ISSUE-16115/testsuite.sh
+new file mode 100755
+index 0000000000..695896ab7b
+--- /dev/null
++++ b/test/TEST-51-ISSUE-16115/testsuite.sh
+@@ -0,0 +1,19 @@
++#!/usr/bin/env bash
++set -ex
++set -o pipefail
++
++systemd-analyze log-level debug
++systemd-analyze log-target console
++
++systemctl start repro-1
++systemctl start repro-2
++sleep 5 # wait a bit in case there are restarts so we can count them below
++
++[[ "$(systemctl show repro-1 --value -p NRestarts)" == "0" ]]
++[[ "$(systemctl show repro-2 --value -p NRestarts)" == "0" ]]
++
++systemd-analyze log-level info
++
++echo OK > /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?= <msekleta@redhat.com>
+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?= <msekleta@redhat.com>
+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 <lennart@poettering.net>
+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?= <zbyszek@in.waw.pl>
+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 <balint.reczey@canonical.com>
+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?= <zbyszek@in.waw.pl>
+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?= <msekleta@redhat.com>
+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.</para>
+           </listitem>
+         </varlistentry>
++        <varlistentry>
++          <term><command>freeze <replaceable>PATTERN</replaceable>…</command></term>
++
++          <listitem>
++            <para>Freeze one or more units specified on the
++            command line using cgroup freezer</para>
++
++            <para>Freezing the unit will cause all processes contained within the cgroup corresponding to the unit
++            to be suspended. Being suspended means that unit's processes won't be scheduled to run on CPU until thawed.
++            Note that this command is supported only on systems that use unified cgroup hierarchy. Unit is automatically
++            thawed just before we execute a job against the unit, e.g. before the unit is stopped.</para>
++          </listitem>
++        </varlistentry>
++        <varlistentry>
++          <term><command>thaw <replaceable>PATTERN</replaceable>…</command></term>
++
++          <listitem>
++            <para>Thaw (unfreeze) one or more units specified on the
++            command line.</para>
++
++            <para>This is the inverse operation to the <command>freeze</command> command and resumes the execution of
++            processes in the unit's cgroup.</para>
++          </listitem>
++        </varlistentry>
+         <varlistentry>
+           <term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
+ 
+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 <msekleta@redhat.com>
+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?= <msekleta@redhat.com>
+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?= <msekleta@redhat.com>
+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 <<EOF
++[Unit]
++Description=Testsuite service
++
++[Service]
++ExecStart=/bin/bash -x /testsuite.sh
++Type=oneshot
++StandardOutput=tty
++StandardError=tty
++EOF
++        cat > $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 <dtardon@redhat.com>
+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?= <msekleta@redhat.com>
+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?= <msekleta@redhat.com>
+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?= <msekleta@redhat.com>
+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 <rmetrich@redhat.com> 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 <lennart@poettering.net>
+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 <lennart@poettering.net>
+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 @@
+     <filename>cryptsetup-pre.target</filename>,
+     <filename>cryptsetup.target</filename>,
+     <filename>ctrl-alt-del.target</filename>,
++    <filename>boot-complete.target</filename>,
+     <filename>default.target</filename>,
+     <filename>emergency.target</filename>,
+     <filename>exit.target</filename>,
+@@ -646,6 +647,28 @@
+           </para>
+         </listitem>
+       </varlistentry>
++        <varlistentry>
++          <term><filename>boot-complete.target</filename></term>
++          <listitem>
++            <para>This target is intended as generic synchronization point for services that shall determine or act on
++            whether the boot process completed successfully. Order units that are required to succeed for a boot process
++            to be considered successful before this unit, and add a <varname>Requires=</varname> dependency from the
++            target unit to them. Order units that shall only run when the boot process is considered successful after the
++            target unit and pull in the target from it, also with <varname>Requires=</varname>. Note that by default this
++            target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by
++            units that want to run only on successful boots.</para>
++
++            <para>See
++            <citerefentry><refentrytitle>systemd-boot-check-no-failures.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
++            for a service that implements a generic system health check and orders itself before
++            <filename>boot-complete.target</filename>.</para>
++
++            <para>See
++            <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
++            for a service that propagates boot success information to the boot loader, and orders itself after
++            <filename>boot-complete.target</filename>.</para>
++          </listitem>
++        </varlistentry>
+       <varlistentry>
+         <term><filename>syslog.socket</filename></term>
+         <listitem>
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 <lennart@poettering.net>
+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/SPECS/systemd.spec b/SPECS/systemd.spec
index c09114d..a8be922 100644
--- a/SPECS/systemd.spec
+++ b/SPECS/systemd.spec
@@ -13,7 +13,7 @@
 Name:           systemd
 Url:            http://www.freedesktop.org/wiki/Software/systemd
 Version:        239
-Release:        31%{?dist}.2
+Release:        40%{?dist}
 # For a breakdown of the licensing, see README
 License:        LGPLv2+ and MIT and GPLv2+
 Summary:        System and Service Manager
@@ -392,31 +392,98 @@ 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-pid1-fix-the-names-of-AllowedCPUs-and-AllowedMemoryN.patch
-Patch0344: 0344-core-fix-re-realization-of-cgroup-siblings.patch
-Patch0345: 0345-basic-use-comma-as-separator-in-cpuset-cgroup-cpu-ra.patch
-Patch0346: 0346-swap-finish-the-secondary-swap-units-jobs-if-deactiv.patch
-Patch0347: 0347-pid1-add-new-kernel-cmdline-arg-systemd.cpu_affinity.patch
-Patch0348: 0348-sd-journal-close-journal-files-that-were-deleted-by-.patch
-Patch0349: 0349-sd-journal-remove-the-dead-code-and-actually-fix-146.patch
-Patch0350: 0350-test-do-not-use-global-variable-to-pass-error.patch
-Patch0351: 0351-test-install-libraries-required-by-tests.patch
-Patch0352: 0352-test-introduce-install_zoneinfo.patch
-Patch0353: 0353-test-replace-duplicated-Makefile-by-symbolic-link.patch
-Patch0354: 0354-test-add-paths-of-keymaps-in-install_keymaps.patch
-Patch0355: 0355-test-make-install_keymaps-optionally-install-more-ke.patch
-Patch0356: 0356-test-fs-util-skip-some-tests-when-running-in-unprivi.patch
-Patch0357: 0357-test-process-util-skip-several-verifications-when-ru.patch
-Patch0358: 0358-test-execute-also-check-python3-is-installed-or-not.patch
-Patch0359: 0359-test-execute-skip-several-tests-when-running-in-cont.patch
-Patch0360: 0360-test-introduce-test_is_running_from_builddir.patch
-Patch0361: 0361-test-make-test-catalog-relocatable.patch
-Patch0362: 0362-test-parallelize-tasks-in-TEST-24-UNIT-TESTS.patch
-Patch0363: 0363-test-try-to-determine-QEMU_SMP-dynamically.patch
-Patch0364: 0364-test-store-coredumps-in-journal.patch
-Patch0365: 0365-udev-rules-make-tape-changers-also-apprear-in-dev-ta.patch
-Patch0366: 0366-nspawn-move-payload-to-sub-cgroup-first-then-sync-cg.patch
-Patch0367: 0367-nspawn-chown-the-legacy-hierarchy-when-it-s-used-in-.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
 
 
 %ifarch %{ix86} x86_64 aarch64
@@ -677,6 +744,7 @@ CONFIGURE_OPTS=(
         -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
@@ -786,6 +854,9 @@ 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
@@ -793,7 +864,7 @@ rm -f %{buildroot}%{_prefix}/lib/systemd/system/local-fs.target.wants/tmp.mount
 # here.
 python3 %{SOURCE2} %buildroot <<EOF
 %ghost %config(noreplace) /etc/crypttab
-%ghost /etc/udev/hwdb.bin
+%ghost %verify (not mode) /etc/udev/hwdb.bin
 /etc/inittab
 /etc/yum/protected.d/systemd.conf
 /usr/lib/systemd/purge-nobody-user
@@ -801,16 +872,16 @@ python3 %{SOURCE2} %buildroot <<EOF
 %ghost %config(noreplace) /etc/X11/xorg.conf.d/00-keyboard.conf
 %ghost %attr(0664,root,utmp) /var/run/utmp
 %ghost %attr(0664,root,utmp) /var/log/wtmp
-%ghost %attr(0600,root,utmp) /var/log/btmp
+%ghost %attr(0660,root,utmp) /var/log/btmp
 %ghost %config(noreplace) /etc/hostname
 %ghost %config(noreplace) /etc/localtime
 %ghost %config(noreplace) /etc/locale.conf
-%ghost %config(noreplace) /etc/machine-id
+%ghost %config(noreplace) %attr(0444,root,root) /etc/machine-id
 %ghost %config(noreplace) /etc/machine-info
-%config(noreplace) %{_sysconfdir}/rc.d/rc.local
+%verify(owner group) %config(noreplace) %{_sysconfdir}/rc.d/rc.local
 %{_sysconfdir}/rc.local
-%ghost %dir /var/cache/private
-%ghost %dir /var/lib/private
+%ghost %dir %attr(0700,root,root) /var/cache/private
+%ghost %dir %attr(0700,root,root) /var/lib/private
 %ghost %dir /var/lib/private/systemd
 %ghost %dir /var/lib/private/systemd/journal-upload
 %ghost /var/lib/private/systemd/journal-upload/state
@@ -819,11 +890,11 @@ python3 %{SOURCE2} %buildroot <<EOF
 %ghost %dir /var/lib/systemd/coredump
 %ghost /var/lib/systemd/journal-upload
 %ghost %dir /var/lib/systemd/linger
-%ghost /var/lib/systemd/random-seed
+%ghost %attr(0600,root,root) /var/lib/systemd/random-seed
 %ghost %dir /var/lib/systemd/rfkill
-%ghost %dir /var/log/journal
+%ghost %verify (not mode group md5 mtime) %dir /var/log/journal
 %ghost %dir /var/log/journal/remote
-%ghost %dir /var/log/private
+%ghost %dir %attr(0700,root,root) /var/log/private
 EOF
 
 %check
@@ -1041,54 +1112,130 @@ fi
 %files tests -f .file-list-tests
 
 %changelog
-* Fri May 22 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-31.2
-- test: do not use global variable to pass error (#1836979)
-- test: install libraries required by tests (#1836979)
-- test: introduce install_zoneinfo() (#1836979)
-- test: replace duplicated Makefile by symbolic link (#1836979)
-- test: add paths of keymaps in install_keymaps() (#1836979)
-- test: make install_keymaps() optionally install more keymaps (#1836979)
-- test-fs-util: skip some tests when running in unprivileged container (#1836979)
-- test-process-util: skip several verifications when running in unprivileged container (#1836979)
-- test-execute: also check python3 is installed or not (#1836979)
-- test-execute: skip several tests when running in container (#1836979)
-- test: introduce test_is_running_from_builddir() (#1836979)
-- test: make test-catalog relocatable (#1836979)
-- test: parallelize tasks in TEST-24-UNIT-TESTS (#1836979)
-- test: try to determine QEMU_SMP dynamically (#1836979)
-- test: store coredumps in journal (#1836979)
-- udev-rules: make tape-changers also apprear in /dev/tape/by-path/ (#1836981)
-- nspawn: move payload to sub-cgroup first, then sync cgroup trees (#1837423)
-- nspawn: chown() the legacy hierarchy when it's used in a container (#1837423)
-
-* Mon May 11 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-31.1
-- sd-journal: close journal files that were deleted by journald before we've setup inotify watch (#1826217)
-- sd-journal: remove the dead code and actually fix #14695 (#1826217)
-
-* Mon Apr 27 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-31
-- swap: finish the secondary swap units' jobs if deactivation of the primary swap unit fails (#1821372)
-- pid1: add new kernel cmdline arg systemd.cpu_affinity= (#1827621)
-
-* Wed Apr 15 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-30
-- pid1: fix the names of AllowedCPUs= and AllowedMemoryNodes= (#1824129)
-- core: fix re-realization of cgroup siblings (#1824129)
-- basic: use comma as separator in cpuset cgroup cpu ranges (#1824129)
-
-* Mon Mar 23 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-29
-- cryptsetup: Treat key file errors as a failed password attempt (#1763155)
-
-* Wed Mar 11 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-28
+* Fri Aug 28 2020 systemd maintenance team <systemd-maint@redhat.com> - 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 <systemd-maint@redhat.com> - 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 <systemd-maint@redhat.com> - 239-38
+- spec: fix rpm verification (#1702300)
+
+* Wed Jul 08 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-37
+- spec: don't package /etc/systemd/system/dbus-org.freedesktop.resolve1.service (#1844465)
+
+* Fri Jun 26 2020 systemd maintenance team <systemd-maint@redhat.com> - 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 <systemd-maint@redhat.com> - 239-35
+- spec: fix rpm verification (#1702300)
+
+* Thu Jun 18 2020 systemd maintenance team <systemd-maint@redhat.com> - 239-34
+- spec: fix rpm verification (#1702300)
+
+* Tue Jun 09 2020 systemd maintenance team <systemd-maint@redhat.com> - 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 <systemd-maint@redhat.com> - 239-32
 - pid1: fix DefaultTasksMax initialization (#1809037)
-- cgroup: make sure that cpuset is supported on cgroup  v2 and disabled with v1 (#1808940)
+- 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 more reliable (#1808940)
-- test: support MPOL_LOCAL matching in unpatched strace  versions (#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: 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 <systemd-maint@redhat.com> - 239-27
 - cgroup: introduce support for cgroup v2 CPUSET controller (#1724617)