From 2f7d91d7cecbe806594b5b8bcdb007fc58c7e531 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 31 2020 12:25:43 +0000 Subject: import systemd-219-73.el7_8.5 --- diff --git a/SOURCES/0802-core-when-restarting-services-don-t-close-fds.patch b/SOURCES/0802-core-when-restarting-services-don-t-close-fds.patch new file mode 100644 index 0000000..91ef314 --- /dev/null +++ b/SOURCES/0802-core-when-restarting-services-don-t-close-fds.patch @@ -0,0 +1,112 @@ +From 017d1bc2a34946336dbafcc1c9c390ea45a2a3bd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 22 Oct 2016 22:16:02 -0400 +Subject: [PATCH] core: when restarting services, don't close fds + +We would close all the stored fds in service_release_resources(), which of +course broke the whole concept of storing fds over service restart. + +Fixes #4408. + +(cherry picked from commit f0bfbfac43b7faa68ef1bb2ad659c191b9ec85d2) +(cherry picked from commit 4c271437cd695c31e76adb191013009689a7797c) +Resolves: #1803802 +--- + src/core/service.c | 22 +++++++++++++++------- + src/core/unit.c | 6 ++++-- + src/core/unit.h | 2 +- + 3 files changed, 20 insertions(+), 10 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index e538280bad..3c2f69a003 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -258,7 +258,17 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { + free(fs); + } + +-static void service_release_resources(Unit *u) { ++static void service_release_fd_store(Service *s) { ++ assert(s); ++ ++ log_unit_debug(UNIT(s)->id, "Releasing all stored fds"); ++ while (s->fd_store) ++ service_fd_store_unlink(s->fd_store); ++ ++ assert(s->n_fd_store == 0); ++} ++ ++static void service_release_resources(Unit *u, bool inactive) { + Service *s = SERVICE(u); + + assert(s); +@@ -266,12 +276,10 @@ static void service_release_resources(Unit *u) { + if (!s->fd_store) + return; + +- log_debug("Releasing all resources for %s", u->id); +- +- while (s->fd_store) +- service_fd_store_unlink(s->fd_store); ++ log_unit_debug(u->id, "Releasing resources."); + +- assert(s->n_fd_store == 0); ++ if (inactive) ++ service_release_fd_store(s); + } + + static void service_done(Unit *u) { +@@ -319,7 +327,7 @@ static void service_done(Unit *u) { + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + +- service_release_resources(u); ++ service_release_resources(u, true); + } + + static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { +diff --git a/src/core/unit.c b/src/core/unit.c +index 0dc66203a4..22e31a76b3 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -285,6 +285,7 @@ int unit_set_description(Unit *u, const char *description) { + + bool unit_may_gc(Unit *u) { + UnitActiveState state; ++ bool inactive; + assert(u); + + /* Checks whether the unit is ready to be unloaded for garbage collection. +@@ -302,16 +303,17 @@ bool unit_may_gc(Unit *u) { + return false; + + state = unit_active_state(u); ++ inactive = state == UNIT_INACTIVE; + + /* If the unit is inactive and failed and no job is queued for + * it, then release its runtime resources */ + if (UNIT_IS_INACTIVE_OR_FAILED(state) && + UNIT_VTABLE(u)->release_resources) +- UNIT_VTABLE(u)->release_resources(u); ++ UNIT_VTABLE(u)->release_resources(u, inactive); + + /* But we keep the unit object around for longer when it is + * referenced or configured to not be gc'ed */ +- if (state != UNIT_INACTIVE) ++ if (!inactive) + return false; + + if (UNIT_VTABLE(u)->no_gc) +diff --git a/src/core/unit.h b/src/core/unit.h +index 719fc95260..232be8164f 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -360,7 +360,7 @@ struct UnitVTable { + + /* When the unit is not running and no job for it queued we + * shall release its runtime resources */ +- void (*release_resources)(Unit *u); ++ void (*release_resources)(Unit *u, bool inactive); + + /* Return true when this unit is suitable for snapshotting */ + bool (*check_snapshot)(Unit *u); diff --git a/SOURCES/0803-unit-rework-a-bit-how-we-keep-the-service-fdstore-fr.patch b/SOURCES/0803-unit-rework-a-bit-how-we-keep-the-service-fdstore-fr.patch new file mode 100644 index 0000000..3dc70e2 --- /dev/null +++ b/SOURCES/0803-unit-rework-a-bit-how-we-keep-the-service-fdstore-fr.patch @@ -0,0 +1,170 @@ +From 9684908410868cb590ad74f4747bdbf9b5790abb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 13 Nov 2017 15:08:49 +0100 +Subject: [PATCH] unit: rework a bit how we keep the service fdstore from being + destroyed during service restart + +When preparing for a restart we quickly go through the DEAD/INACTIVE +service state before entering AUTO_RESTART. When doing this, we need to +make sure we don't destroy the FD store. Previously this was done by +checking the failure state of the unit, and keeping the FD store around +when the unit failed, under the assumption that the restart logic will +then get into action. + +This is not entirely correct howver, as there might be failure states +that will no result in restarts. + +With this commit we slightly alter the logic: a ref counter for the fd +store is added, that is increased right before we handle the restart +logic, and decreased again right-after. + +This should ensure that the fdstore lives exactly as long as it needs. + +Follow-up for f0bfbfac43b7faa68ef1bb2ad659c191b9ec85d2. + +(cherry picked from commit 7eb2a8a1259043e107ebec94e30ed160a93f40a7) +(cherry picked from commit e2636bde0f07319d0d35262dac6ff2638ba4e598) +Resolves: #1803802 +--- + src/core/service.c | 23 ++++++++++++++++++----- + src/core/service.h | 1 + + src/core/unit.c | 12 ++++-------- + src/core/unit.h | 5 ++--- + 4 files changed, 25 insertions(+), 16 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 3c2f69a003..e32cdf4594 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -261,6 +261,9 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { + static void service_release_fd_store(Service *s) { + assert(s); + ++ if (s->n_keep_fd_store > 0) ++ return; ++ + log_unit_debug(UNIT(s)->id, "Releasing all stored fds"); + while (s->fd_store) + service_fd_store_unlink(s->fd_store); +@@ -268,7 +271,7 @@ static void service_release_fd_store(Service *s) { + assert(s->n_fd_store == 0); + } + +-static void service_release_resources(Unit *u, bool inactive) { ++static void service_release_resources(Unit *u) { + Service *s = SERVICE(u); + + assert(s); +@@ -278,8 +281,7 @@ static void service_release_resources(Unit *u, bool inactive) { + + log_unit_debug(u->id, "Releasing resources."); + +- if (inactive) +- service_release_fd_store(s); ++ service_release_fd_store(s); + } + + static void service_done(Unit *u) { +@@ -327,7 +329,7 @@ static void service_done(Unit *u) { + + s->timer_event_source = sd_event_source_unref(s->timer_event_source); + +- service_release_resources(u, true); ++ service_release_resources(u); + } + + static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { +@@ -1330,6 +1332,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + if (f != SERVICE_SUCCESS) + s->result = f; + ++ /* Make sure service_release_resources() doesn't destroy our FD store, while we are changing through ++ * 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); + + if (s->result != SERVICE_SUCCESS) { +@@ -1351,12 +1357,19 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + (!IN_SET(s->main_exec_status.code, CLD_KILLED, CLD_DUMPED) || !set_contains(s->restart_prevent_status.signal, INT_TO_PTR(s->main_exec_status.status)))) { + + r = service_arm_timer(s, s->restart_usec); +- if (r < 0) ++ if (r < 0) { ++ s->n_keep_fd_store--; + goto fail; ++ } + + service_set_state(s, SERVICE_AUTO_RESTART); + } + ++ /* The new state is in effect, let's decrease the fd store ref counter again. Let's also readd us to the GC ++ * queue, so that the fd store is possibly gc'ed again */ ++ s->n_keep_fd_store--; ++ unit_add_to_gc_queue(UNIT(s)); ++ + s->forbid_restart = false; + + /* We want fresh tmpdirs in case service is started again immediately */ +diff --git a/src/core/service.h b/src/core/service.h +index 82938a1fc4..e5753f1f4c 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -215,6 +215,7 @@ struct Service { + ServiceFDStore *fd_store; + unsigned n_fd_store; + unsigned n_fd_store_max; ++ unsigned n_keep_fd_store; + }; + + extern const UnitVTable service_vtable; +diff --git a/src/core/unit.c b/src/core/unit.c +index 22e31a76b3..eff9fdbe70 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -285,7 +285,6 @@ int unit_set_description(Unit *u, const char *description) { + + bool unit_may_gc(Unit *u) { + UnitActiveState state; +- bool inactive; + assert(u); + + /* Checks whether the unit is ready to be unloaded for garbage collection. +@@ -303,17 +302,14 @@ bool unit_may_gc(Unit *u) { + return false; + + state = unit_active_state(u); +- inactive = state == UNIT_INACTIVE; + +- /* If the unit is inactive and failed and no job is queued for +- * it, then release its runtime resources */ ++ /* If the unit is inactive and failed and no job is queued for it, then release its runtime resources */ + if (UNIT_IS_INACTIVE_OR_FAILED(state) && + UNIT_VTABLE(u)->release_resources) +- UNIT_VTABLE(u)->release_resources(u, inactive); ++ UNIT_VTABLE(u)->release_resources(u); + +- /* But we keep the unit object around for longer when it is +- * referenced or configured to not be gc'ed */ +- if (!inactive) ++ /* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */ ++ if (state != UNIT_INACTIVE) + return false; + + if (UNIT_VTABLE(u)->no_gc) +diff --git a/src/core/unit.h b/src/core/unit.h +index 232be8164f..3b0fd8d9df 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -358,9 +358,8 @@ struct UnitVTable { + * even though nothing references it and it isn't active in any way. */ + bool (*may_gc)(Unit *u); + +- /* When the unit is not running and no job for it queued we +- * shall release its runtime resources */ +- void (*release_resources)(Unit *u, bool inactive); ++ /* When the unit is not running and no job for it queued we shall release its runtime resources */ ++ void (*release_resources)(Unit *u); + + /* Return true when this unit is suitable for snapshotting */ + bool (*check_snapshot)(Unit *u); diff --git a/SOURCES/0804-tests-add-basic-journal-test.patch b/SOURCES/0804-tests-add-basic-journal-test.patch new file mode 100644 index 0000000..1455a61 --- /dev/null +++ b/SOURCES/0804-tests-add-basic-journal-test.patch @@ -0,0 +1,145 @@ +From c53791854050661d34417f12755837abd74d213a Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Tue, 17 Nov 2015 11:21:23 +0000 +Subject: [PATCH] tests: add basic journal test + +(cherry picked from commit 1c36b4a73b876258fbe01fbe9bc9b750b7dcc9ce) +(cherry picked from commit e974b9915ccc7bf56d23fd6fc67631990d893c89) +Resolves: #1803802 +--- + test/TEST-04-JOURNAL/Makefile | 1 + + test/TEST-04-JOURNAL/test-journal.sh | 18 +++++++ + test/TEST-04-JOURNAL/test.sh | 76 ++++++++++++++++++++++++++++ + test/test-functions | 2 +- + 4 files changed, 96 insertions(+), 1 deletion(-) + create mode 120000 test/TEST-04-JOURNAL/Makefile + create mode 100755 test/TEST-04-JOURNAL/test-journal.sh + create mode 100755 test/TEST-04-JOURNAL/test.sh + +diff --git a/test/TEST-04-JOURNAL/Makefile b/test/TEST-04-JOURNAL/Makefile +new file mode 120000 +index 0000000000..e9f93b1104 +--- /dev/null ++++ b/test/TEST-04-JOURNAL/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh +new file mode 100755 +index 0000000000..c75f396ceb +--- /dev/null ++++ b/test/TEST-04-JOURNAL/test-journal.sh +@@ -0,0 +1,18 @@ ++#!/bin/bash ++ ++set -x ++set -e ++set -o pipefail ++ ++# Test stdout stream ++ ++# Skip empty lines ++ID=$(journalctl --new-id128 | sed -n 2p) ++>/expected ++printf $'\n\n\n' | systemd-cat -t "$ID" --level-prefix false ++journalctl --flush ++journalctl -b -o cat -t "$ID" >/output ++cmp /expected /output ++ ++touch /testok ++exit 0 +diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh +new file mode 100755 +index 0000000000..e37cb7d412 +--- /dev/null ++++ b/test/TEST-04-JOURNAL/test.sh +@@ -0,0 +1,76 @@ ++#!/bin/bash ++# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- ++# ex: ts=8 sw=4 sts=4 et filetype=sh ++TEST_DESCRIPTION="Journal-related tests" ++ ++. $TEST_BASE_DIR/test-functions ++ ++check_result_qemu() { ++ ret=1 ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ [[ -e $TESTDIR/root/testok ]] && ret=0 ++ [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR ++ cp -a $TESTDIR/root/var/log/journal $TESTDIR ++ umount $TESTDIR/root ++ [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed ++ ls -l $TESTDIR/journal/*/*.journal ++ test -s $TESTDIR/failed && ret=$(($ret+1)) ++ return $ret ++} ++ ++test_run() { ++ if run_qemu; then ++ check_result_qemu || return 1 ++ else ++ dwarn "can't run QEMU, skipping" ++ fi ++ if check_nspawn; then ++ run_nspawn ++ check_result_nspawn || return 1 ++ else ++ dwarn "can't run systemd-nspawn, skipping" ++ fi ++ return 0 ++} ++ ++test_setup() { ++ create_empty_image ++ mkdir -p $TESTDIR/root ++ mount ${LOOPDEV}p1 $TESTDIR/root ++ ++ # Create what will eventually be our root filesystem onto an overlay ++ ( ++ LOG_LEVEL=5 ++ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) ++ ++ setup_basic_environment ++ ++ # setup the testsuite service ++ cat >$initdir/etc/systemd/system/testsuite.service </dev/null ++ [[ $LOOPDEV ]] && losetup -d $LOOPDEV ++ return 0 ++} ++ ++do_test "$@" +diff --git a/test/test-functions b/test/test-functions +index e50ce556fd..d5e9650903 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -12,7 +12,7 @@ if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then + ROOTLIBDIR=/usr/lib/systemd + fi + +-BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe chmod chown ln" ++BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe chmod chown ln sed cmp tee" + 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" + + function find_qemu_bin() { diff --git a/SOURCES/0805-tests-add-regression-test-for-systemctl-restart-syst.patch b/SOURCES/0805-tests-add-regression-test-for-systemctl-restart-syst.patch new file mode 100644 index 0000000..725665c --- /dev/null +++ b/SOURCES/0805-tests-add-regression-test-for-systemctl-restart-syst.patch @@ -0,0 +1,54 @@ +From e3468af46bd16e3550c25a0770f47f0bf21780f3 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Wed, 30 Dec 2015 03:33:43 +0000 +Subject: [PATCH] tests: add regression test for `systemctl restart + systemd-journald` + +See https://github.com/systemd/systemd/issues/2236 + +(cherry picked from commit 3889613ec6bc54b9e02955f62b9d5c5b571c3d4b) +(cherry picked from commit 4dc893c03fe9c56a3d3070fb8583f5584014aa49) +Resolves: #1803802 +--- + test/TEST-04-JOURNAL/test-journal.sh | 8 ++++++++ + test/TEST-04-JOURNAL/test.sh | 9 +++++++++ + 2 files changed, 17 insertions(+) + +diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh +index c75f396ceb..701b0cf724 100755 +--- a/test/TEST-04-JOURNAL/test-journal.sh ++++ b/test/TEST-04-JOURNAL/test-journal.sh +@@ -14,5 +14,13 @@ journalctl --flush + journalctl -b -o cat -t "$ID" >/output + cmp /expected /output + ++# Don't lose streams on restart ++systemctl start forever-print-hola ++sleep 3 ++systemctl restart systemd-journald ++sleep 3 ++systemctl stop forever-print-hola ++[[ ! -f "/i-lose-my-logs" ]] ++ + touch /testok + exit 0 +diff --git a/test/TEST-04-JOURNAL/test.sh b/test/TEST-04-JOURNAL/test.sh +index e37cb7d412..6aea67ba4e 100755 +--- a/test/TEST-04-JOURNAL/test.sh ++++ b/test/TEST-04-JOURNAL/test.sh +@@ -55,6 +55,15 @@ After=multi-user.target + [Service] + ExecStart=/test-journal.sh + Type=oneshot ++EOF ++ ++ cat >$initdir/etc/systemd/system/forever-print-hola.service < +Date: Thu, 20 Oct 2016 13:18:12 +0000 +Subject: [PATCH] tests: add test that journald keeps fds over termination by + signal + +This test fails before previous commit, and passes with it. + +(cherry picked from commit bff653e3970bb79832568ae86b095ee530b62302) +(cherry picked from commit ee8f69ae5ddac6f05c56ea7dbcb76fbbb2e355ee) +Resolves: #1803802 +--- + test/TEST-04-JOURNAL/test-journal.sh | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh +index 701b0cf724..d0b05c46d6 100755 +--- a/test/TEST-04-JOURNAL/test-journal.sh ++++ b/test/TEST-04-JOURNAL/test-journal.sh +@@ -22,5 +22,13 @@ sleep 3 + systemctl stop forever-print-hola + [[ ! -f "/i-lose-my-logs" ]] + ++# https://github.com/systemd/systemd/issues/4408 ++rm -f /i-lose-my-logs ++systemctl start forever-print-hola ++sleep 3 ++systemctl kill --signal=SIGKILL systemd-journald ++sleep 3 ++[[ ! -f "/i-lose-my-logs" ]] ++ + touch /testok + exit 0 diff --git a/SOURCES/0807-journal-do-not-trigger-assertion-when-journal_file_c.patch b/SOURCES/0807-journal-do-not-trigger-assertion-when-journal_file_c.patch new file mode 100644 index 0000000..0910c9f --- /dev/null +++ b/SOURCES/0807-journal-do-not-trigger-assertion-when-journal_file_c.patch @@ -0,0 +1,50 @@ +From d8c33d7a4e0d9b4375159776913f5c3fd2905c21 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 7 Jan 2020 11:22:26 +0100 +Subject: [PATCH] journal: do not trigger assertion when journal_file_close() + get NULL + +We generally expect destructors to not complain if a NULL argument is passed. + +Closes #12400. + +(cherry picked from commit c377a6f3ad3d9bed4ce7e873e8e9ec6b1650c57d) +(cherry picked from commit 248925c68092bab7ce0231449176db153f55d818) +Resolves: #1807798 +--- + src/journal/journal-file.c | 3 ++- + src/journal/journald-server.c | 7 ++----- + 2 files changed, 4 insertions(+), 6 deletions(-) + +diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c +index 586f620e21..cf8dad3fcd 100644 +--- a/src/journal/journal-file.c ++++ b/src/journal/journal-file.c +@@ -129,7 +129,8 @@ int journal_file_set_offline(JournalFile *f) { + } + + void journal_file_close(JournalFile *f) { +- assert(f); ++ if (!f) ++ return; + + #ifdef HAVE_GCRYPT + /* Write the final tag */ +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index aaabb2f7ab..ffe2daa7be 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -1933,11 +1933,8 @@ void server_done(Server *s) { + while (s->stdout_streams) + stdout_stream_free(s->stdout_streams); + +- if (s->system_journal) +- journal_file_close(s->system_journal); +- +- if (s->runtime_journal) +- journal_file_close(s->runtime_journal); ++ (void) journal_file_close(s->system_journal); ++ (void) journal_file_close(s->runtime_journal); + + while ((f = ordered_hashmap_steal_first(s->user_journals))) + journal_file_close(f); diff --git a/SOURCES/0808-mount-don-t-propagate-errors-from-mount_setup_unit-f.patch b/SOURCES/0808-mount-don-t-propagate-errors-from-mount_setup_unit-f.patch new file mode 100644 index 0000000..a8de335 --- /dev/null +++ b/SOURCES/0808-mount-don-t-propagate-errors-from-mount_setup_unit-f.patch @@ -0,0 +1,53 @@ +From 5b7b538fcd268288cea8d8b7f16f839afbf74f39 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 28 Nov 2018 12:41:44 +0100 +Subject: [PATCH] mount: don't propagate errors from mount_setup_unit() further + up + +If we can't process a specific line in /proc/self/mountinfo we should +log about it (which we do), but this should not affect other lines, nor +further processing of mount units. Let's keep these failures local. + +Fixes: #10874 +(cherry picked from commit ba0d56f55f2073164799be714b5bd1aad94d059a) +Resolves: #1809159 +--- + src/core/mount.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 3167bd6bb1..126038c27f 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1571,7 +1571,7 @@ fail: + static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { + _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL; +- int r = 0; ++ int r; + + assert(m); + +@@ -1587,7 +1587,6 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { + if (r < 0) + return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); + +- r = 0; + for (;;) { + const char *device, *path, *options, *fstype; + _cleanup_free_ const char *d = NULL, *p = NULL; +@@ -1615,12 +1614,10 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { + + (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags); + +- k = mount_setup_unit(m, d, p, options, fstype, set_flags); +- if (r == 0 && k < 0) +- r = k; ++ (void) mount_setup_unit(m, d, p, options, fstype, set_flags); + } + +- return r; ++ return 0; + } + + static void mount_shutdown(Manager *m) { diff --git a/SOURCES/0809-mount-when-allocating-a-Mount-object-based-on-proc-s.patch b/SOURCES/0809-mount-when-allocating-a-Mount-object-based-on-proc-s.patch new file mode 100644 index 0000000..d689d1a --- /dev/null +++ b/SOURCES/0809-mount-when-allocating-a-Mount-object-based-on-proc-s.patch @@ -0,0 +1,35 @@ +From 58407f55ccfc83eef2bd14e672d13213ef93069d Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 28 Nov 2018 14:51:04 +0100 +Subject: [PATCH] mount: when allocating a Mount object based on + /proc/self/mountinfo mark it so + +Let's set 'from_proc_self_mountinfo' right away, since we know its from +there. This is important so that when the load queue is dispatched (and +thus mount_load() called) this +fact is already known. + +(cherry picked from commit 6d7e89b07065b8c49af47aaf1ccc336d017fc3a2) +Related: #1809159 +--- + src/core/mount.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 126038c27f..34c4b548ad 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1480,6 +1480,13 @@ static int mount_setup_unit( + } + } + ++ /* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the time we load ++ * the unit file for it (and thus add in extra deps right after) we know what source to attributes the deps ++ * to.*/ ++ MOUNT(u)->from_proc_self_mountinfo = true; ++ ++ /* We have only allocated the stub now, let's enqueue this unit for loading now, so that everything else is ++ * loaded in now. */ + unit_add_to_load_queue(u); + changed = true; + } else { diff --git a/SOURCES/0810-core-enforce-a-ratelimiter-when-stopping-units-due-t.patch b/SOURCES/0810-core-enforce-a-ratelimiter-when-stopping-units-due-t.patch new file mode 100644 index 0000000..3d94ec4 --- /dev/null +++ b/SOURCES/0810-core-enforce-a-ratelimiter-when-stopping-units-due-t.patch @@ -0,0 +1,60 @@ +From f57ee0c7862260f3c7054b224f360cdef5140685 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 19 May 2015 16:00:24 +0200 +Subject: [PATCH] core: enforce a ratelimiter when stopping units due to + StopWhenUnneeded=1 + +Otherwise we might end up in an endless stop loop. + +http://lists.freedesktop.org/archives/systemd-devel/2015-April/030224.html +(cherry picked from commit bea355dac94e82697aa98e25d80ee4248263bf92) +(cherry picked from commit 6c72a575c7c61e17c8e96b23042f7b4a5ac39339) + +Related: #1810576 +--- + src/core/unit.c | 10 ++++++++++ + src/core/unit.h | 3 +++ + 2 files changed, 13 insertions(+) + +diff --git a/src/core/unit.c b/src/core/unit.c +index eff9fdbe70..583b9fae28 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -97,6 +97,8 @@ Unit *unit_new(Manager *m, size_t size) { + u->on_failure_job_mode = JOB_REPLACE; + u->sigchldgen = 0; + ++ RATELIMIT_INIT(u->check_unneeded_ratelimit, 10 * USEC_PER_SEC, 16); ++ + return u; + } + +@@ -1594,6 +1596,14 @@ static void unit_check_unneeded(Unit *u) { + if (unit_active_or_pending(other)) + return; + ++ /* If stopping a unit fails continously we might enter a stop ++ * loop here, hence stop acting on the service being ++ * unnecessary after a while. */ ++ if (!ratelimit_test(&u->check_unneeded_ratelimit)) { ++ log_unit_warning(u->id, "Unit not needed anymore, but not stopping since we tried this too often recently."); ++ return; ++ } ++ + log_unit_info(u->id, "Unit %s is not needed anymore. Stopping.", u->id); + + /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ +diff --git a/src/core/unit.h b/src/core/unit.h +index 3b0fd8d9df..29353ea81c 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -185,6 +185,9 @@ struct Unit { + /* Error code when we didn't manage to load the unit (negative) */ + int load_error; + ++ /* Make sure we never enter endless loops with the check unneeded logic */ ++ RateLimit check_unneeded_ratelimit; ++ + /* Cached unit file state and preset */ + UnitFileState unit_file_state; + int unit_file_preset; diff --git a/SOURCES/0811-core-rework-StopWhenUnneeded-logic.patch b/SOURCES/0811-core-rework-StopWhenUnneeded-logic.patch new file mode 100644 index 0000000..cc78e66 --- /dev/null +++ b/SOURCES/0811-core-rework-StopWhenUnneeded-logic.patch @@ -0,0 +1,343 @@ +From c7c8a7648623a25c00dc585bd42b9037a20396c8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 9 Aug 2018 16:26:27 +0200 +Subject: [PATCH] core: rework StopWhenUnneeded= logic + +Previously, we'd act immediately on StopWhenUnneeded= when a unit state +changes. With this rework we'll maintain a queue instead: whenever +there's the chance that StopWhenUneeded= might have an effect we enqueue +the unit, and process it later when we have nothing better to do. + +This should make the implementation a bit more reliable, as the unit notify event +cannot immediately enqueue tons of side-effect jobs that might +contradict each other, but we do so only in a strictly ordered fashion, +from the main event loop. + +This slightly changes the check when to consider a unit "unneeded". +Previously, we'd assume that a unit in "deactivating" state could also +be cleaned up. With this new logic we'll only consider units unneeded +that are fully up and have no job queued. This means that whenever +there's something pending for a unit we won't clean it up. + +(cherry picked from commit a3c1168ac293f16d9343d248795bb4c246aaff4a) +(cherry picked from commit 65ebf5e453846e29ab5894670a83b5d8b942c858) + +Resolves: #1810576 +--- + src/core/manager.c | 43 +++++++++++++++ + src/core/manager.h | 3 ++ + src/core/unit.c | 132 ++++++++++++++++++++++++--------------------- + src/core/unit.h | 7 +++ + 4 files changed, 124 insertions(+), 61 deletions(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index 4c87ad8a2f..f0553b4df9 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -961,6 +961,45 @@ static unsigned manager_dispatch_gc_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->id, "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_test(&u->check_unneeded_ratelimit)) { ++ log_unit_warning(u->id, "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, true, &error, NULL); ++ if (r < 0) ++ log_unit_warning_errno(u->id, 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; + +@@ -977,6 +1016,7 @@ static void manager_clear_jobs_and_units(Manager *m) { + assert(!m->dbus_job_queue); + assert(!m->cleanup_queue); + assert(!m->gc_queue); ++ assert(!m->stop_when_unneeded_queue); + + assert(hashmap_isempty(m->jobs)); + assert(hashmap_isempty(m->units)); +@@ -2259,6 +2299,9 @@ int manager_loop(Manager *m) { + if (manager_dispatch_cgroup_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 cfc564dfb6..f9280956e9 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -117,6 +117,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; + + /* We use two hash tables here, since the same PID might be +diff --git a/src/core/unit.c b/src/core/unit.c +index 583b9fae28..dc2df4c89c 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -380,6 +380,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, Set *s) { + Iterator i; + Unit *other; +@@ -544,6 +560,9 @@ void unit_free(Unit *u) { + u->manager->n_in_gc_queue--; + } + ++ if (u->in_stop_when_unneeded_queue) ++ LIST_REMOVE(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u); ++ + if (u->on_console) + manager_unref_console(u->manager); + +@@ -1565,49 +1584,68 @@ bool unit_can_reload(Unit *u) { + return UNIT_VTABLE(u)->can_reload(u); + } + +-static void unit_check_unneeded(Unit *u) { +- Iterator i; +- Unit *other; ++bool unit_is_unneeded(Unit *u) { ++ static const UnitDependency deps[] = { ++ UNIT_REQUIRED_BY, ++ UNIT_REQUIRED_BY_OVERRIDABLE, ++ UNIT_WANTED_BY, ++ UNIT_BOUND_BY, ++ }; ++ 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; + +- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) +- if (unit_active_or_pending(other)) +- return; ++ for (j = 0; j < ELEMENTSOF(deps); j++) { ++ Unit *other; ++ Iterator i; + +- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) +- if (unit_active_or_pending(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. */ + +- SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) +- if (unit_active_or_pending(other)) +- return; ++ SET_FOREACH(other, u->dependencies[deps[j]], i) { ++ if (u->job) ++ return false; + +- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) +- if (unit_active_or_pending(other)) +- return; +- +- /* If stopping a unit fails continously we might enter a stop +- * loop here, hence stop acting on the service being +- * unnecessary after a while. */ +- if (!ratelimit_test(&u->check_unneeded_ratelimit)) { +- log_unit_warning(u->id, "Unit not needed anymore, but not stopping since we tried this too often recently."); +- return; ++ if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) ++ return false; ++ } + } + +- log_unit_info(u->id, "Unit %s is not needed anymore. Stopping.", u->id); ++ return true; ++} + +- /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ +- manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); ++static void check_unneeded_dependencies(Unit *u) { ++ ++ static const UnitDependency deps[] = { ++ UNIT_REQUIRES, ++ UNIT_REQUIRES_OVERRIDABLE, ++ UNIT_REQUISITE, ++ UNIT_REQUISITE_OVERRIDABLE, ++ 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; ++ ++ SET_FOREACH(other, u->dependencies[deps[j]], i) ++ unit_add_to_stop_when_unneeded_queue(other); ++ } + } + + static void unit_check_binds_to(Unit *u) { +@@ -1693,34 +1731,6 @@ static void retroactively_stop_dependencies(Unit *u) { + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL); + } + +-static void check_unneeded_dependencies(Unit *u) { +- Iterator i; +- Unit *other; +- +- assert(u); +- assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u))); +- +- /* Garbage collect services that might not be needed anymore, if enabled */ +- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- SET_FOREACH(other, u->dependencies[UNIT_WANTS], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) +- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- unit_check_unneeded(other); +- SET_FOREACH(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; +@@ -1898,7 +1908,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su + } + + /* 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) { +@@ -1959,7 +1969,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su + if (u->manager->n_reloading <= 0) { + /* 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 +diff --git a/src/core/unit.h b/src/core/unit.h +index 29353ea81c..38c97397ee 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -165,6 +165,9 @@ 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 */ +@@ -235,6 +238,7 @@ struct Unit { + bool in_gc_queue:1; + bool in_cgroup_queue:1; + bool in_target_deps_queue:1; ++ bool in_stop_when_unneeded_queue:1; + + bool sent_dbus_new_signal:1; + +@@ -508,6 +512,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); +@@ -627,6 +632,8 @@ int unit_make_transient(Unit *u); + + int unit_require_mounts_for(Unit *u, const char *path); + ++bool unit_is_unneeded(Unit *u); ++ + pid_t unit_control_pid(Unit *u); + pid_t unit_main_pid(Unit *u); + diff --git a/SOURCES/0812-fix-the-fix-for-1691511.patch b/SOURCES/0812-fix-the-fix-for-1691511.patch new file mode 100644 index 0000000..95f38e7 --- /dev/null +++ b/SOURCES/0812-fix-the-fix-for-1691511.patch @@ -0,0 +1,53 @@ +From 659b97b93d93ad05732831f6f097cf6f2a9c6410 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Mon, 16 Mar 2020 09:37:22 +0100 +Subject: [PATCH] fix the fix for #1691511 + +The fix for #1691511 should have been used only for existing mounts +(mount_setup_existing_unit() in current master). + +(cherry picked from commit 4584818bea998d257a166c238b829025b6138e55) + +Related: #1809159 +--- + src/core/mount.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/core/mount.c b/src/core/mount.c +index 34c4b548ad..56fda1b73f 100644 +--- a/src/core/mount.c ++++ b/src/core/mount.c +@@ -1416,6 +1416,7 @@ static int mount_setup_unit( + bool load_extras = false; + MountParameters *p; + bool delete, changed = false; ++ bool just_mounted; + Unit *u; + int r; + +@@ -1489,6 +1490,7 @@ static int mount_setup_unit( + * loaded in now. */ + unit_add_to_load_queue(u); + changed = true; ++ just_mounted = true; + } else { + delete = false; + +@@ -1518,6 +1520,8 @@ static int mount_setup_unit( + load_extras = true; + changed = true; + } ++ ++ just_mounted = !MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->just_mounted; + } + + w = strdup(what); +@@ -1537,7 +1541,7 @@ static int mount_setup_unit( + + if (set_flags) { + MOUNT(u)->is_mounted = true; +- MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->just_mounted; ++ MOUNT(u)->just_mounted = just_mounted; + MOUNT(u)->just_changed = changed; + } + diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec index fbe65dd..46894de 100644 --- a/SPECS/systemd.spec +++ b/SPECS/systemd.spec @@ -7,7 +7,7 @@ Name: systemd Url: http://www.freedesktop.org/wiki/Software/systemd Version: 219 -Release: 73%{?dist}.1 +Release: 73%{?dist}.5 # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: A System and Service Manager @@ -840,6 +840,17 @@ Patch0798: 0798-Call-getgroups-to-know-size-of-supplementary-groups-.patch Patch0799: 0799-udev-introduce-CONST-key-name.patch Patch0800: 0800-path-stop-watching-path-specs-once-we-triggered-the-.patch Patch0801: 0801-unit-fix-potential-use-of-cgroup_path-after-free-whe.patch +Patch0802: 0802-core-when-restarting-services-don-t-close-fds.patch +Patch0803: 0803-unit-rework-a-bit-how-we-keep-the-service-fdstore-fr.patch +Patch0804: 0804-tests-add-basic-journal-test.patch +Patch0805: 0805-tests-add-regression-test-for-systemctl-restart-syst.patch +Patch0806: 0806-tests-add-test-that-journald-keeps-fds-over-terminat.patch +Patch0807: 0807-journal-do-not-trigger-assertion-when-journal_file_c.patch +Patch0808: 0808-mount-don-t-propagate-errors-from-mount_setup_unit-f.patch +Patch0809: 0809-mount-when-allocating-a-Mount-object-based-on-proc-s.patch +Patch0810: 0810-core-enforce-a-ratelimiter-when-stopping-units-due-t.patch +Patch0811: 0811-core-rework-StopWhenUnneeded-logic.patch +Patch0812: 0812-fix-the-fix-for-1691511.patch %global num_patches %{lua: c=0; for i,p in ipairs(patches) do c=c+1; end; print(c);} @@ -1817,6 +1828,25 @@ fi %{_mandir}/man8/systemd-resolved.* %changelog +* Tue Mar 17 2020 systemd maintenance team - 219-73.5 +- core: enforce a ratelimiter when stopping units due to StopWhenUnneeded=1 (#1810576) +- core: rework StopWhenUnneeded= logic (#1810576) +- fix the fix for #1691511 (#1809159) + +* Thu Mar 12 2020 systemd maintenance team - 219-73.4 +- mount: don't propagate errors from mount_setup_unit() further up (#1809159) +- mount: when allocating a Mount object based on /proc/self/mountinfo mark it so (#1809159) + +* Thu Mar 05 2020 systemd maintenance team - 219-73.3 +- journal: do not trigger assertion when journal_file_close() get NULL (#1807798) + +* Tue Feb 18 2020 systemd maintenance team - 219-73.2 +- core: when restarting services, don't close fds (#1803802) +- unit: rework a bit how we keep the service fdstore from being destroyed during service restart (#1803802) +- tests: add basic journal test (#1803802) +- tests: add regression test for `systemctl restart systemd-journald` (#1803802) +- tests: add test that journald keeps fds over termination by signal (#1803802) + * Fri Nov 29 2019 Lukas Nykryn - 219-73.1 - unit: fix potential use of cgroup_path after free() when freeing unit (#1760149)