summaryrefslogtreecommitdiffstats
path: root/debian/patches
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch21
-rw-r--r--debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch74
-rw-r--r--debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch42
-rw-r--r--debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch69
-rw-r--r--debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch57
-rw-r--r--debian/patches/debian/Skip-flaky-test_resolved_domain_restricted_dns-in-network.patch24
-rw-r--r--debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch1072
-rw-r--r--debian/patches/debian/localectl-disable-keymap-support.patch76
-rw-r--r--debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch34
-rw-r--r--debian/patches/series9
10 files changed, 1478 insertions, 0 deletions
diff --git a/debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch b/debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch
new file mode 100644
index 0000000..d99ea42
--- /dev/null
+++ b/debian/patches/debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch
@@ -0,0 +1,21 @@
+From: Tollef Fog Heen <tfheen@err.no>
+Date: Tue, 5 Jun 2012 20:59:36 +0200
+Subject: Bring tmpfiles.d/tmp.conf in line with Debian defaults
+
+Closes: #675422
+---
+ tmpfiles.d/tmp.conf | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/tmpfiles.d/tmp.conf b/tmpfiles.d/tmp.conf
+index fe5225d..39cb5cc 100644
+--- a/tmpfiles.d/tmp.conf
++++ b/tmpfiles.d/tmp.conf
+@@ -8,5 +8,5 @@
+ # See tmpfiles.d(5) for details
+
+ # Clear tmp directories separately, to make them easier to override
+-q /tmp 1777 root root 10d
+-q /var/tmp 1777 root root 30d
++D /tmp 1777 root root -
++#q /var/tmp 1777 root root 30d
diff --git a/debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch b/debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch
new file mode 100644
index 0000000..1c839bd
--- /dev/null
+++ b/debian/patches/debian/Downgrade-a-couple-of-warnings-to-debug.patch
@@ -0,0 +1,74 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Tue, 16 Feb 2021 00:18:50 +0100
+Subject: Downgrade a couple of warnings to debug
+
+If a package still ships only a SysV init script or if a service file or
+tmpfile uses /var/run, downgrade those messages to debug. We can use
+lintian to detect those issues.
+For service files and tmpfiles in /etc, keep the warning, as those files
+are typically added locally and aren't checked by lintian.
+
+Closes: #981407
+---
+ src/core/load-fragment.c | 4 +++-
+ src/sysv-generator/sysv-generator.c | 2 +-
+ src/tmpfiles/tmpfiles.c | 4 +++-
+ 3 files changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
+index f442bd8..a92f683 100644
+--- a/src/core/load-fragment.c
++++ b/src/core/load-fragment.c
+@@ -544,6 +544,7 @@ static int patch_var_run(
+
+ const char *e;
+ char *z;
++ int log_level;
+
+ e = path_startswith(*path, "/var/run/");
+ if (!e)
+@@ -553,7 +554,8 @@ static int patch_var_run(
+ if (!z)
+ return log_oom();
+
+- log_syntax(unit, LOG_NOTICE, filename, line, 0,
++ log_level = path_startswith(filename, "/etc") ? LOG_NOTICE : LOG_DEBUG;
++ log_syntax(unit, log_level, filename, line, 0,
+ "%s= references a path below legacy directory /var/run/, updating %s → %s; "
+ "please update the unit file accordingly.", lvalue, *path, z);
+
+diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
+index 4485e2e..d0e8ed8 100644
+--- a/src/sysv-generator/sysv-generator.c
++++ b/src/sysv-generator/sysv-generator.c
+@@ -762,7 +762,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
+ if (!fpath)
+ return log_oom();
+
+- log_struct(LOG_WARNING,
++ log_struct(LOG_DEBUG,
+ LOG_MESSAGE("SysV service '%s' lacks a native systemd unit file. "
+ "%s Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it safe, robust and future-proof. "
+ "%s This compatibility logic is deprecated, expect removal soon. %s",
+diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
+index 230ec09..d513d40 100644
+--- a/src/tmpfiles/tmpfiles.c
++++ b/src/tmpfiles/tmpfiles.c
+@@ -3266,6 +3266,7 @@ static int specifier_expansion_from_arg(const Specifier *specifier_table, Item *
+ static int patch_var_run(const char *fname, unsigned line, char **path) {
+ const char *k;
+ char *n;
++ int log_level;
+
+ assert(path);
+ assert(*path);
+@@ -3291,7 +3292,8 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
+ /* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
+ * there's no immediate need for action by the user. However, in the interest of making things less confusing
+ * to the user, let's still inform the user that these snippets should really be updated. */
+- log_syntax(NULL, LOG_NOTICE, fname, line, 0,
++ log_level = path_startswith(fname, "/etc") ? LOG_NOTICE : LOG_DEBUG;
++ log_syntax(NULL, log_level, fname, line, 0,
+ "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.",
+ *path, n);
+
diff --git a/debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch b/debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch
new file mode 100644
index 0000000..a93e7a3
--- /dev/null
+++ b/debian/patches/debian/Make-run-lock-tmpfs-an-API-fs.patch
@@ -0,0 +1,42 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Fri, 5 Sep 2014 01:15:16 +0200
+Subject: Make /run/lock tmpfs an API fs
+
+The /run/lock directory is world-writable in Debian due to historic
+reasons. To avoid user processes filling up /run, we mount a separate
+tmpfs for /run/lock. As this directory needs to be available during
+early boot, we make it an API fs.
+
+Drop it from tmpfiles.d/legacy.conf to not clobber the permissions.
+
+Closes: #751392
+---
+ src/shared/mount-setup.c | 2 ++
+ tmpfiles.d/legacy.conf.in | 1 -
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c
+index 1226ca1..2d59955 100644
+--- a/src/shared/mount-setup.c
++++ b/src/shared/mount-setup.c
+@@ -101,6 +101,8 @@ static const MountPoint mount_table[] = {
+ #endif
+ { "tmpfs", "/run", "tmpfs", "mode=0755" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ NULL, MNT_FATAL|MNT_IN_CONTAINER },
++ { "tmpfs", "/run/lock", "tmpfs", "mode=1777,size=5242880", MS_NOSUID|MS_NOEXEC|MS_NODEV,
++ NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate,memory_recursiveprot", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ check_recursiveprot_supported, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
+ { "cgroup2", "/sys/fs/cgroup", "cgroup2", "nsdelegate", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+diff --git a/tmpfiles.d/legacy.conf.in b/tmpfiles.d/legacy.conf.in
+index 4f2c0d7..fb1d6bf 100644
+--- a/tmpfiles.d/legacy.conf.in
++++ b/tmpfiles.d/legacy.conf.in
+@@ -10,7 +10,6 @@
+ # These files are considered legacy and are unnecessary on legacy-free
+ # systems.
+
+-d /run/lock 0755 root root -
+ L /var/lock - - - - ../run/lock
+ {% if CREATE_LOG_DIRS %}
+ L /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
diff --git a/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch b/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch
new file mode 100644
index 0000000..267c2b4
--- /dev/null
+++ b/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch
@@ -0,0 +1,69 @@
+From: Martin Pitt <martin.pitt@ubuntu.com>
+Date: Sat, 27 Feb 2016 12:27:06 +0100
+Subject: Revert "core: set RLIMIT_CORE to unlimited by default"
+
+Partially revert commit 15a900327ab as this completely breaks core dumps
+without systemd-coredump. It's also contradicting core(8), and it's not
+systemd's place to redefine the kernel definitions of core files.
+
+Commit bdfd7b2c now honours the process' RLIMIT_CORE for systemd-coredump. This
+isn't what RLIMIT_CORE is supposed to do (it limits the size of the core
+*file*, but the kernel deliberately ignores it for piping), so set a static
+2^63 core size limit for systemd-coredump to go back to the previous behaviour
+(otherwise the change above would break systemd-coredump).
+
+Bug-Debian: https://bugs.debian.org/815020
+---
+ src/core/main.c | 18 ------------------
+ sysctl.d/50-coredump.conf.in | 2 +-
+ 2 files changed, 1 insertion(+), 19 deletions(-)
+
+diff --git a/src/core/main.c b/src/core/main.c
+index 3f71cc0..876c7d5 100644
+--- a/src/core/main.c
++++ b/src/core/main.c
+@@ -1653,22 +1653,6 @@ static void cmdline_take_random_seed(void) {
+ "This functionality should not be used outside of testing environments.");
+ }
+
+-static void initialize_coredump(bool skip_setup) {
+- if (getpid_cached() != 1)
+- return;
+-
+- /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour
+- * the limit) will process core dumps for system services by default. */
+- if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
+- log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m");
+-
+- /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
+- * until the systemd-coredump tool is enabled via sysctl. However it can be changed via the kernel
+- * command line later so core dumps can still be generated during early startup and in initrd. */
+- if (!skip_setup)
+- disable_coredumps();
+-}
+-
+ static void initialize_core_pattern(bool skip_setup) {
+ int r;
+
+@@ -2922,8 +2906,6 @@ int main(int argc, char *argv[]) {
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
+ }
+
+- initialize_coredump(skip_setup);
+-
+ r = fixup_environment();
+ if (r < 0) {
+ log_struct_errno(LOG_EMERG, r,
+diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in
+index 90c080b..1730841 100644
+--- a/sysctl.d/50-coredump.conf.in
++++ b/sysctl.d/50-coredump.conf.in
+@@ -13,7 +13,7 @@
+ # the core dump.
+ #
+ # See systemd-coredump(8) and core(5).
+-kernel.core_pattern=|{{LIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h
++kernel.core_pattern=|{{LIBEXECDIR}}/systemd-coredump %P %u %g %s %t 9223372036854775808 %h
+
+ # Allow 16 coredumps to be dispatched in parallel by the kernel.
+ # We collect metadata from /proc/%P/, and thus need to make sure the crashed
diff --git a/debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch b/debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch
new file mode 100644
index 0000000..6353b8b
--- /dev/null
+++ b/debian/patches/debian/Skip-filesystem-check-if-already-done-by-the-initram.patch
@@ -0,0 +1,57 @@
+From: Nis Martensen <nis.martensen@web.de>
+Date: Tue, 19 Jan 2016 22:01:43 +0100
+Subject: Skip filesystem check if already done by the initramfs
+
+Newer versions of initramfs-tools already fsck and mount / and /usr in
+the initramfs. Skip the filesystem check in this case.
+
+Based on a previous patch by Michael Biebl <biebl@debian.org>.
+
+Closes: #782522
+Closes: #810748
+---
+ src/fstab-generator/fstab-generator.c | 11 ++++++++---
+ units/systemd-fsck-root.service.in | 1 +
+ 2 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
+index 016f3ba..f63a1d1 100644
+--- a/src/fstab-generator/fstab-generator.c
++++ b/src/fstab-generator/fstab-generator.c
+@@ -519,6 +519,7 @@ static int add_mount(
+ _cleanup_strv_free_ char **wanted_by = NULL, **required_by = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
++ struct stat sb;
+
+ assert(what);
+ assert(where);
+@@ -604,9 +605,13 @@ static int add_mount(
+ fprintf(f, "Before=%s\n", target_unit);
+
+ if (passno != 0) {
+- r = generator_write_fsck_deps(f, dest, what, where, fstype);
+- if (r < 0)
+- return r;
++ if (streq(where, "/usr") && stat("/run/initramfs/fsck-usr", &sb) == 0)
++ ; /* skip /usr fsck if it has already been checked in the initramfs */
++ else {
++ r = generator_write_fsck_deps(f, dest, what, where, fstype);
++ if (r < 0)
++ return r;
++ }
+ }
+
+ r = generator_write_blockdev_dependency(f, what);
+diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in
+index ca9c7ce..27783a9 100644
+--- a/units/systemd-fsck-root.service.in
++++ b/units/systemd-fsck-root.service.in
+@@ -16,6 +16,7 @@ Before=local-fs.target shutdown.target
+ Wants=systemd-fsckd.socket
+ After=systemd-fsckd.socket
+ ConditionPathIsReadWrite=!/
++ConditionPathExists=!/run/initramfs/fsck-root
+ OnFailure=emergency.target
+ OnFailureJobMode=replace-irreversibly
+
diff --git a/debian/patches/debian/Skip-flaky-test_resolved_domain_restricted_dns-in-network.patch b/debian/patches/debian/Skip-flaky-test_resolved_domain_restricted_dns-in-network.patch
new file mode 100644
index 0000000..a57191d
--- /dev/null
+++ b/debian/patches/debian/Skip-flaky-test_resolved_domain_restricted_dns-in-network.patch
@@ -0,0 +1,24 @@
+From: Michael Biebl <biebl@debian.org>
+Date: Tue, 13 Dec 2022 00:32:17 +0100
+Subject: Skip flaky test_resolved_domain_restricted_dns in networkd-test.py
+
+This test is part of DnsmasqClientTest and does not work reliably under
+LXC/debci, so skip it for the time being.
+
+Closes: #1025908
+---
+ test/networkd-test.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/test/networkd-test.py b/test/networkd-test.py
+index 512137c..1bf1538 100755
+--- a/test/networkd-test.py
++++ b/test/networkd-test.py
+@@ -639,6 +639,7 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase):
+ with open(path) as f:
+ sys.stdout.write('\n\n---- {} ----\n{}\n------\n\n'.format(os.path.basename(path), f.read()))
+
++ @unittest.skip("test is flaky: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1025908")
+ def test_resolved_domain_restricted_dns(self):
+ '''resolved: domain-restricted DNS servers'''
+
diff --git a/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch b/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch
new file mode 100644
index 0000000..7ec4439
--- /dev/null
+++ b/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch
@@ -0,0 +1,1072 @@
+From: Didier Roche <didrocks@ubuntu.com>
+Date: Fri, 22 May 2015 13:04:38 +0200
+Subject: fsckd daemon for inter-fsckd communication
+
+Global logic:
+Add systemd-fsckd multiplexer which accepts multiple (via systemd-fsck's
+/run/systemd/fsck.progress socket) fsck instances to connect to it and sends
+progress report. systemd-fsckd then computes and writes to /dev/console the
+number of devices currently being checked and the minimum fsck progress.
+
+Plymouth and user interaction:
+Forward the progress to plymouth and support canellation of in progress fsck.
+Try to connect and send to plymouth (if running) some checked report progress,
+using direct plymouth protocole.
+
+Update message is the following:
+fsckd:<num_devices>:<progress>:<string>
+* num_devices corresponds to the current number of devices being checked (int)
+* progress corresponds to the current minimum percentage of all devices being
+ checked (float, from 0 to 100)
+* string is a translated message ready to be displayed by the plymouth theme
+ displaying the information above. It can be overridden by plymouth themes
+ supporting i18n.
+
+Grab in fsckd plymouth watch key Control+C, and propagate this cancel request
+to systemd-fsck which will terminate fsck.
+
+Send a message to signal to user what key we are grabbing for fsck cancel.
+
+Message is: fsckd-cancel-msg:<string>
+Where string is a translated string ready to be displayed by the plymouth theme
+indicating that Control+C can be used to cancel current checks. It can be
+overridden (matching only fsckd-cancel-msg prefix) for themes supporting i18n.
+
+Misc:
+systemd-fsckd stops on idle when no fsck is connected.
+Add man page explaining the plymouth theme protocol, usage of the daemon
+as well as the socket activation part. Adapt existing fsck man page.
+
+Note that fsckd had lived in the upstream tree for a while, but was removed.
+More information at
+http://lists.freedesktop.org/archives/systemd-devel/2015-April/030175.html
+-
+---
+ man/rules/meson.build | 1 +
+ man/systemd-fsckd.service.xml | 162 +++++++++
+ meson.build | 1 +
+ po/POTFILES.in | 1 +
+ src/fsckd/fsckd.c | 705 +++++++++++++++++++++++++++++++++++++
+ src/fsckd/meson.build | 8 +
+ units/meson.build | 2 +
+ units/systemd-fsck-root.service.in | 2 +
+ units/systemd-fsck@.service.in | 3 +-
+ units/systemd-fsckd.service.in | 17 +
+ units/systemd-fsckd.socket | 15 +
+ 11 files changed, 916 insertions(+), 1 deletion(-)
+ create mode 100644 man/systemd-fsckd.service.xml
+ create mode 100644 src/fsckd/fsckd.c
+ create mode 100644 src/fsckd/meson.build
+ create mode 100644 units/systemd-fsckd.service.in
+ create mode 100644 units/systemd-fsckd.socket
+
+diff --git a/man/rules/meson.build b/man/rules/meson.build
+index 5dc3e08..5feb43d 100644
+--- a/man/rules/meson.build
++++ b/man/rules/meson.build
+@@ -933,6 +933,7 @@ manpages = [
+ '8',
+ ['systemd-fsck', 'systemd-fsck-root.service', 'systemd-fsck-usr.service'],
+ ''],
++ ['systemd-fsckd.service', '8', ['systemd-fsckd.socket', 'systemd-fsckd'], ''],
+ ['systemd-fstab-generator', '8', [], ''],
+ ['systemd-getty-generator', '8', [], ''],
+ ['systemd-gpt-auto-generator', '8', [], 'HAVE_BLKID'],
+diff --git a/man/systemd-fsckd.service.xml b/man/systemd-fsckd.service.xml
+new file mode 100644
+index 0000000..b7ad58d
+--- /dev/null
++++ b/man/systemd-fsckd.service.xml
+@@ -0,0 +1,162 @@
++<?xml version="1.0"?>
++<!--*-nxml-*-->
++<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
++<!--
++ This file is part of systemd.
++
++ Copyright 2015 Canonical
++
++ systemd is free software; you can redistribute it and/or modify it
++ under the terms of the GNU Lesser General Public License as published by
++ the Free Software Foundation; either version 2.1 of the License, or
++ (at your option) any later version.
++
++ systemd is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public License
++ along with systemd; If not, see <http://www.gnu.org/licenses/>.
++-->
++<refentry id="systemd-fsckd.service" xmlns:xi="http://www.w3.org/2001/XInclude">
++
++ <refentryinfo>
++ <title>systemd-fsckd.service</title>
++ <productname>systemd</productname>
++
++ <authorgroup>
++ <author>
++ <contrib>Developer</contrib>
++ <firstname>Didier</firstname>
++ <surname>Roche</surname>
++ <email>didrocks@ubuntu.com</email>
++ </author>
++ </authorgroup>
++ </refentryinfo>
++
++ <refmeta>
++ <refentrytitle>systemd-fsckd.service</refentrytitle>
++ <manvolnum>8</manvolnum>
++ </refmeta>
++
++ <refnamediv>
++ <refname>systemd-fsckd.service</refname>
++ <refname>systemd-fsckd.socket</refname>
++ <refname>systemd-fsckd</refname>
++ <refpurpose>File system check progress reporting</refpurpose>
++ </refnamediv>
++
++ <refsynopsisdiv>
++ <para><filename>systemd-fsckd.service</filename></para>
++ <para><filename>systemd-fsckd.socket</filename></para>
++ <para><filename>/usr/lib/systemd/systemd-fsckd</filename></para>
++ </refsynopsisdiv>
++
++ <refsect1>
++ <title>Description</title>
++
++ <para><filename>systemd-fsckd.service</filename> is a service responsible
++ for receiving file system check progress, and communicating some
++ consolidated data to console and plymouth (if running). It also handles
++ possible check cancellations.</para>
++
++ <para><command>systemd-fsckd</command> receives messages about file
++ system check progress from <command>fsck</command> through an
++ UNIX domain socket. It can display the progress of the least advanced
++ fsck as well as the total number of devices being checked in parallel
++ to the console. It will also send progress messages to plymouth.
++ Both the raw data and translated messages are sent, so compiled
++ plymouth themes can use the raw data to display custom messages, and
++ scripted themes, not supporting i18n, can display the translated
++ versions.</para>
++
++ <para><command>systemd-fsckd</command> will instruct plymouth to grab
++ Control+C keypresses. When the key is pressed, running checks will be
++ terminated. It will also cancel any newly connected fsck instances for
++ the lifetime of <filename>systemd-fsckd</filename>.</para>
++ </refsect1>
++
++ <refsect1>
++ <title>Protocol for communication with plymouth</title>
++
++ <para><filename>systemd-fsckd</filename> passes the
++ following messages to the theme:</para>
++
++ <para>Progress update, sent as a plymouth update message:
++ <literal>fsckd:&lt;num_devices&gt;:&lt;progress&gt;:&lt;string&gt;</literal>
++ <variablelist>
++ <varlistentry>
++ <term><literal>&lt;num_devices&gt;</literal></term>
++ <listitem><para>the current number of devices
++ being checked (int)</para></listitem>
++ </varlistentry>
++ <varlistentry>
++ <term><literal>&lt;progress&gt;</literal></term>
++ <listitem><para>the current minimum percentage of
++ all devices being checking (float, from 0 to 100)</para></listitem>
++ </varlistentry>
++ <varlistentry>
++ <term><literal>&lt;string&gt;</literal></term>
++ <listitem><para>a translated message ready to be displayed
++ by the plymouth theme displaying the data above. It can be overridden
++ by themes supporting i18n.</para></listitem>
++ </varlistentry>
++ </variablelist>
++ </para>
++
++ <para>Cancel message, sent as a traditional plymouth message:
++ <literal>fsckd-cancel-msg:&lt;string&gt;</literal>
++ <variablelist>
++ <varlistentry>
++ <term><literal>&lt;strings&gt;</literal></term>
++ <listitem><para>a translated string ready to be displayed
++ by the plymouth theme indicating that Control+C can be used to cancel
++ current checks. It can be overridden (matching only
++ <literal>fsckd-cancel-msg</literal> prefix)
++ by themes supporting i18n.</para></listitem>
++ </varlistentry>
++ </variablelist>
++ </para>
++ </refsect1>
++
++ <refsect1>
++ <title>Options</title>
++
++ <para>The following options are understood:</para>
++
++ <variablelist>
++ <xi:include href="standard-options.xml" xpointer="help" />
++ <xi:include href="standard-options.xml" xpointer="version" />
++ </variablelist>
++
++ </refsect1>
++
++ <refsect1>
++ <title>Exit status</title>
++
++ <para>On success, 0 is returned, a non-zero failure
++ code otherwise. Note that the daemon stays idle for
++ a while to accept new <filename>fsck</filename>
++ connections before exiting.</para>
++ </refsect1>
++
++ <refsect1>
++ <title>See Also</title>
++ <para>
++ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd-fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry><refentrytitle>systemd-quotacheck.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.cramfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.ext4</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.fat</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.hfsplus</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.minix</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.ntfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
++ <citerefentry project='man-pages'><refentrytitle>fsck.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
++ </para>
++ </refsect1>
++
++</refentry>
+diff --git a/meson.build b/meson.build
+index f1f4e7e..24f5bd2 100644
+--- a/meson.build
++++ b/meson.build
+@@ -2152,6 +2152,7 @@ subdir('src/environment-d-generator')
+ subdir('src/escape')
+ subdir('src/firstboot')
+ subdir('src/fsck')
++subdir('src/fsckd')
+ subdir('src/fstab-generator')
+ subdir('src/getty-generator')
+ subdir('src/gpt-auto-generator')
+diff --git a/po/POTFILES.in b/po/POTFILES.in
+index 16899fd..6bba341 100644
+--- a/po/POTFILES.in
++++ b/po/POTFILES.in
+@@ -13,3 +13,4 @@ src/portable/org.freedesktop.portable1.policy
+ src/resolve/org.freedesktop.resolve1.policy
+ src/timedate/org.freedesktop.timedate1.policy
+ src/core/dbus-unit.c
++src/fsckd/fsckd.c
+diff --git a/src/fsckd/fsckd.c b/src/fsckd/fsckd.c
+new file mode 100644
+index 0000000..e9a8019
+--- /dev/null
++++ b/src/fsckd/fsckd.c
+@@ -0,0 +1,705 @@
++/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
++
++/***
++ This file is part of systemd.
++
++ Copyright 2015 Canonical
++
++ Author:
++ Didier Roche <didrocks@ubuntu.com>
++
++ systemd is free software; you can redistribute it and/or modify it
++ under the terms of the GNU Lesser General Public License as published by
++ the Free Software Foundation; either version 2.1 of the License, or
++ (at your option) any later version.
++
++ systemd is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ Lesser General Public License for more details.
++
++ You should have received a copy of the GNU Lesser General Public License
++ along with systemd; If not, see <http://www.gnu.org/licenses/>.
++***/
++
++#include <getopt.h>
++#include <errno.h>
++#include <libintl.h>
++#include <math.h>
++#include <stdbool.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/un.h>
++#include <unistd.h>
++
++#include "sd-daemon.h"
++#include "build.h"
++#include "constants.h"
++#include "sd-event.h"
++#include "log.h"
++#include "list.h"
++#include "macro.h"
++#include "socket-netlink.h"
++#include "socket-util.h"
++#include "fd-util.h"
++#include "string-util.h"
++#include "io-util.h"
++#include "alloc-util.h"
++#include "locale-util.h"
++#include "logarithm.h"
++
++#define FSCKD_SOCKET_PATH "/run/systemd/fsck.progress"
++#define IDLE_TIME_SECONDS 30
++#define PLYMOUTH_REQUEST_KEY "K\2\2\3"
++#define CLIENTS_MAX 128
++
++struct Manager;
++
++typedef struct Client {
++ struct Manager *manager;
++ char *device_name;
++ /* device id refers to "fd <fd>" until it gets a name as "device_name" */
++ char *device_id;
++
++ pid_t fsck_pid;
++ FILE *fsck_f;
++
++ size_t cur;
++ size_t max;
++ int pass;
++
++ double percent;
++
++ bool cancelled;
++ bool bad_input;
++
++ sd_event_source *event_source;
++
++ LIST_FIELDS(struct Client, clients);
++} Client;
++
++typedef struct Manager {
++ sd_event *event;
++
++ LIST_HEAD(Client, clients);
++ unsigned n_clients;
++
++ size_t clear;
++
++ int connection_fd;
++ sd_event_source *connection_event_source;
++
++ bool show_status_console;
++
++ double percent;
++ int numdevices;
++
++ int plymouth_fd;
++ sd_event_source *plymouth_event_source;
++ bool plymouth_cancel_sent;
++
++ bool cancel_requested;
++} Manager;
++
++static Client* client_free(Client *c);
++static Manager* manager_free(Manager *m);
++
++DEFINE_TRIVIAL_CLEANUP_FUNC(Client*, client_free);
++DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
++
++static void init_gettext(void) {
++ setlocale(LC_ALL, "");
++ textdomain(GETTEXT_PACKAGE);
++}
++
++static bool plymouth_running(void) {
++ return access("/run/plymouth/pid", F_OK) >= 0;
++}
++
++static int manager_write_console(Manager *m, const char *message) {
++ _cleanup_fclose_ FILE *console = NULL;
++ int l;
++ size_t j;
++
++ assert(m);
++
++ if (!m->show_status_console)
++ return 0;
++
++ /* Nothing to display, and nothing to clear: return now. */
++ if (message == NULL && m->clear == 0) {
++ return 0;
++ }
++
++ /* Reduce the SAK window by opening and closing console on every request */
++ console = fopen("/dev/console", "we");
++ if (!console)
++ return -errno;
++
++ if (message) {
++ fprintf(console, "\r%s\r%n", message, &l);
++ if (m->clear < (size_t)l)
++ m->clear = (size_t)l;
++ } else {
++ fputc('\r', console);
++ for (j = 0; j < m->clear; j++)
++ fputc(' ', console);
++ fputc('\r', console);
++ }
++ fflush(console);
++
++ return 0;
++}
++
++static double compute_percent(int pass, size_t cur, size_t max) {
++ /* Values stolen from e2fsck */
++
++ static const double pass_table[] = {
++ 0, 70, 90, 92, 95, 100
++ };
++
++ if (pass <= 0)
++ return 0.0;
++
++ if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
++ return 100.0;
++
++ return pass_table[pass-1] +
++ (pass_table[pass] - pass_table[pass-1]) *
++ (double) cur / max;
++}
++
++static int client_request_cancel(Client *c) {
++ assert(c);
++
++ if (c->cancelled)
++ return 0;
++
++ log_info("Request to cancel fsck for %s from fsckd", c->device_id);
++ if (kill(c->fsck_pid, SIGTERM) < 0) {
++ /* ignore the error and consider that cancel was sent if fsck just exited */
++ if (errno != ESRCH)
++ return log_error_errno(errno, "Cannot send cancel to fsck for %s: %m", c->device_id);
++ }
++
++ c->cancelled = true;
++ return 1;
++}
++
++static Client* client_free(Client *c) {
++ assert(c);
++
++ if (c->manager) {
++ LIST_REMOVE(clients, c->manager->clients, c);
++ c->manager->n_clients--;
++ }
++
++ sd_event_source_unref(c->event_source);
++ fclose(c->fsck_f);
++ if (c->device_name)
++ free(c->device_name);
++ if (c->device_id)
++ free(c->device_id);
++ return mfree(c);
++}
++
++static void manager_disconnect_plymouth(Manager *m) {
++ assert(m);
++
++ m->plymouth_event_source = sd_event_source_unref(m->plymouth_event_source);
++ m->plymouth_fd = safe_close(m->plymouth_fd);
++ m->plymouth_cancel_sent = false;
++}
++
++static int manager_plymouth_feedback_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
++ Manager *m = userdata;
++ char buffer[6];
++ ssize_t l;
++
++ assert(m);
++
++ l = read(m->plymouth_fd, buffer, sizeof(buffer));
++ if (l < 0) {
++ log_warning_errno(errno, "Got error while reading from plymouth: %m");
++ manager_disconnect_plymouth(m);
++ return -errno;
++ }
++ if (l == 0) {
++ manager_disconnect_plymouth(m);
++ return 0;
++ }
++
++ if (l > 1 && buffer[0] == '\15')
++ log_error("Message update to plymouth wasn't delivered successfully");
++
++ /* the only answer support type we requested is a key interruption */
++ if (l > 2 && buffer[0] == '\2' && buffer[5] == '\3') {
++ m->cancel_requested = true;
++
++ /* cancel all connected clients */
++ LIST_FOREACH(clients, current, m->clients)
++ client_request_cancel(current);
++ }
++
++ return 0;
++}
++
++static int manager_connect_plymouth(Manager *m) {
++ union sockaddr_union sa = {
++ .un.sun_family = AF_UNIX,
++ .un.sun_path = "\0/org/freedesktop/plymouthd",
++ };
++ int r;
++
++ if (!plymouth_running())
++ return 0;
++
++ /* try to connect or reconnect if sending a message */
++ if (m->plymouth_fd >= 0)
++ return 1;
++
++ m->plymouth_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
++ if (m->plymouth_fd < 0)
++ return log_warning_errno(errno, "Connection to plymouth socket failed: %m");
++
++ if (connect(m->plymouth_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
++ r = log_warning_errno(errno, "Couldn't connect to plymouth: %m");
++ goto fail;
++ }
++
++ r = sd_event_add_io(m->event, &m->plymouth_event_source, m->plymouth_fd, EPOLLIN, manager_plymouth_feedback_handler, m);
++ if (r < 0) {
++ log_warning_errno(r, "Can't listen to plymouth socket: %m");
++ goto fail;
++ }
++
++ return 1;
++
++fail:
++ manager_disconnect_plymouth(m);
++ return r;
++}
++
++static int plymouth_send_message(int plymouth_fd, const char *message, bool update) {
++ _cleanup_free_ char *packet = NULL;
++ int n;
++ char mode = 'M';
++
++ if (update)
++ mode = 'U';
++
++ if (asprintf(&packet, "%c\002%c%s%n", mode, (int) (strlen(message) + 1), message, &n) < 0)
++ return log_oom();
++
++ return loop_write_full(plymouth_fd, packet, n + 1, USEC_INFINITY);
++}
++
++static int manager_send_plymouth_message(Manager *m, const char *message) {
++ const char *plymouth_cancel_message = NULL, *l10n_cancel_message = NULL;
++ int r;
++
++ r = manager_connect_plymouth(m);
++ if (r < 0)
++ return r;
++ /* 0 means that plymouth isn't running, do not send any message yet */
++ else if (r == 0)
++ return 0;
++
++ if (!m->plymouth_cancel_sent) {
++
++ /* Indicate to plymouth that we listen to Ctrl+C */
++ r = loop_write_full(m->plymouth_fd, PLYMOUTH_REQUEST_KEY, sizeof(PLYMOUTH_REQUEST_KEY), USEC_INFINITY);
++ if (r < 0)
++ return log_warning_errno(r, "Can't send to plymouth cancel key: %m");
++
++ m->plymouth_cancel_sent = true;
++
++ l10n_cancel_message = _("Press Ctrl+C to cancel all filesystem checks in progress");
++ plymouth_cancel_message = strjoina("fsckd-cancel-msg:", l10n_cancel_message);
++
++ r = plymouth_send_message(m->plymouth_fd, plymouth_cancel_message, false);
++ if (r < 0)
++ log_warning_errno(r, "Can't send filesystem cancel message to plymouth: %m");
++
++ } else if (m->numdevices == 0) {
++
++ m->plymouth_cancel_sent = false;
++
++ r = plymouth_send_message(m->plymouth_fd, "", false);
++ if (r < 0)
++ log_warning_errno(r, "Can't clear plymouth filesystem cancel message: %m");
++ }
++
++ r = plymouth_send_message(m->plymouth_fd, message, true);
++ if (r < 0)
++ return log_warning_errno(r, "Couldn't send \"%s\" to plymouth: %m", message);
++
++ return 0;
++}
++
++static int manager_update_global_progress(Manager *m) {
++ _cleanup_free_ char *console_message = NULL;
++ _cleanup_free_ char *fsck_message = NULL;
++ int current_numdevices = 0, r;
++ double current_percent = 100;
++
++ /* get the overall percentage */
++ LIST_FOREACH(clients, current, m->clients) {
++ current_numdevices++;
++
++ /* right now, we only keep the minimum % of all fsckd processes. We could in the future trying to be
++ linear, but max changes and corresponds to the pass. We have all the informations into fsckd
++ already if we can treat that in a smarter way. */
++ current_percent = MIN(current_percent, current->percent);
++ }
++
++ /* update if there is anything user-visible to update */
++ if (fabs(current_percent - m->percent) > 0.001 || current_numdevices != m->numdevices) {
++ m->numdevices = current_numdevices;
++ m->percent = current_percent;
++
++ if (asprintf(&console_message,
++ ngettext("Checking in progress on %d disk (%3.1f%% complete)",
++ "Checking in progress on %d disks (%3.1f%% complete)", m->numdevices),
++ m->numdevices, m->percent) < 0)
++ return -ENOMEM;
++
++ if (asprintf(&fsck_message, "fsckd:%d:%3.1f:%s", m->numdevices, m->percent, console_message) < 0)
++ return -ENOMEM;
++
++ r = manager_write_console(m, console_message);
++ if (r < 0)
++ return r;
++
++ /* try to connect to plymouth and send message */
++ r = manager_send_plymouth_message(m, fsck_message);
++ if (r < 0)
++ return r;
++ }
++ return 0;
++}
++
++static int client_progress_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
++ Client *client = userdata;
++ char line[LINE_MAX];
++ Manager *m;
++
++ assert(client);
++ m = client->manager;
++
++ /* check first if we need to cancel this client */
++ if (m->cancel_requested)
++ client_request_cancel(client);
++
++ while (fgets(line, sizeof(line), client->fsck_f) != NULL) {
++ int pass;
++ size_t cur, max;
++ _cleanup_free_ char *device = NULL, *old_device_id = NULL;
++
++ if (sscanf(line, "%i %zu %zu %ms", &pass, &cur, &max, &device) == 4) {
++ if (!client->device_name) {
++ client->device_name = strdup(device);
++ if (!client->device_name) {
++ log_oom();
++ continue;
++ }
++ old_device_id = client->device_id;
++ client->device_id = strdup(device);
++ if (!client->device_id) {
++ log_oom();
++ client->device_id = old_device_id;
++ old_device_id = NULL;
++ continue;
++ }
++ }
++ client->pass = pass;
++ client->cur = cur;
++ client->max = max;
++ client->bad_input = false;
++ client->percent = compute_percent(client->pass, client->cur, client->max);
++ log_debug("Getting progress for %s (%zu, %zu, %d) : %3.1f%%", client->device_id,
++ client->cur, client->max, client->pass, client->percent);
++ } else {
++ if (errno == ENOMEM) {
++ log_oom();
++ continue;
++ }
++
++ /* if previous input was already garbage, kick it off from progress report */
++ if (client->bad_input) {
++ log_warning("Closing connection on incorrect input of fsck connection for %s", client->device_id);
++ client_free(client);
++ manager_update_global_progress(m);
++ return 0;
++ }
++ client->bad_input = true;
++ }
++
++ }
++
++ if (feof(client->fsck_f)) {
++ log_debug("Fsck client %s disconnected", client->device_id);
++ client_free(client);
++ }
++
++ manager_update_global_progress(m);
++ return 0;
++}
++
++static int manager_new_connection_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
++ _cleanup_(client_freep) Client *c = NULL;
++ _cleanup_close_ int new_fsck_fd = -1;
++ _cleanup_fclose_ FILE *new_fsck_f = NULL;
++ struct ucred ucred = {};
++ Manager *m = userdata;
++ int r;
++
++ assert(m);
++
++ /* Initialize and list new clients */
++ new_fsck_fd = accept4(m->connection_fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
++ if (new_fsck_fd < 0) {
++ log_error_errno(errno, "Couldn't accept a new connection: %m");
++ return 0;
++ }
++
++ if (m->n_clients >= CLIENTS_MAX) {
++ log_error("Too many clients, refusing connection.");
++ return 0;
++ }
++
++
++ new_fsck_f = fdopen(new_fsck_fd, "r");
++ if (!new_fsck_f) {
++ log_error_errno(errno, "Couldn't fdopen new connection for fd %d: %m", new_fsck_fd);
++ return 0;
++ }
++ new_fsck_fd = -1;
++
++ r = getpeercred(fileno(new_fsck_f), &ucred);
++ if (r < 0) {
++ log_error_errno(r, "Couldn't get credentials for fsck: %m");
++ return 0;
++ }
++
++ c = new0(Client, 1);
++ if (!c) {
++ log_oom();
++ return 0;
++ }
++
++ c->fsck_pid = ucred.pid;
++ c->fsck_f = new_fsck_f;
++ new_fsck_f = NULL;
++
++ if (asprintf(&(c->device_id), "fd %d", fileno(c->fsck_f)) < 0) {
++ log_oom();
++ return 0;
++ }
++
++ r = sd_event_add_io(m->event, &c->event_source, fileno(c->fsck_f), EPOLLIN, client_progress_handler, c);
++ if (r < 0) {
++ log_oom();
++ return 0;
++ }
++
++ LIST_PREPEND(clients, m->clients, c);
++ m->n_clients++;
++ c->manager = m;
++
++ log_debug("New fsck client connected: %s", c->device_id);
++
++ /* only request the client to cancel now in case the request is dropped by the client (chance to recancel) */
++ if (m->cancel_requested)
++ client_request_cancel(c);
++
++ c = NULL;
++ return 0;
++}
++
++static Manager* manager_free(Manager *m) {
++ if (!m)
++ return NULL;
++
++ /* clear last line */
++ manager_write_console(m, NULL);
++
++ sd_event_source_unref(m->connection_event_source);
++ safe_close(m->connection_fd);
++
++ while (m->clients)
++ client_free(m->clients);
++
++ manager_disconnect_plymouth(m);
++
++ sd_event_unref(m->event);
++
++ return mfree(m);
++}
++
++static int manager_new(Manager **ret, int fd) {
++ _cleanup_(manager_freep) Manager *m = NULL;
++ int r;
++
++ assert(ret);
++
++ m = new0(Manager, 1);
++ if (!m)
++ return -ENOMEM;
++
++ m->plymouth_fd = -1;
++ m->connection_fd = fd;
++ m->percent = 100;
++
++ r = sd_event_default(&m->event);
++ if (r < 0)
++ return r;
++
++ if (access("/run/systemd/show-status", F_OK) >= 0)
++ m->show_status_console = true;
++
++ r = sd_event_add_io(m->event, &m->connection_event_source, fd, EPOLLIN, manager_new_connection_handler, m);
++ if (r < 0)
++ return r;
++
++ *ret = m;
++ m = NULL;
++
++ return 0;
++}
++
++static int run_event_loop_with_timeout(Manager *m, usec_t timeout) {
++ int r, code;
++ sd_event *e = m->event;
++
++ assert(e);
++
++ for (;;) {
++ r = sd_event_get_state(e);
++ if (r < 0)
++ return r;
++ if (r == SD_EVENT_FINISHED)
++ break;
++
++ r = sd_event_run(e, timeout);
++ if (r < 0)
++ return r;
++
++ /* Exit if we reached the idle timeout and no more clients are
++ connected. If there is still an fsck process running but
++ simply slow to send us progress updates, exiting would mean
++ that this fsck process receives SIGPIPE resulting in an
++ aborted file system check. */
++ if (r == 0 && m->n_clients == 0) {
++ sd_event_exit(e, 0);
++ break;
++ }
++ }
++
++ r = sd_event_get_exit_code(e, &code);
++ if (r < 0)
++ return r;
++
++ return code;
++}
++
++static void help(void) {
++ printf("%s [OPTIONS...]\n\n"
++ "Capture fsck progress and forward one stream to plymouth\n\n"
++ " -h --help Show this help\n"
++ " --version Show package version\n",
++ program_invocation_short_name);
++}
++
++static int parse_argv(int argc, char *argv[]) {
++
++ enum {
++ ARG_VERSION = 0x100,
++ ARG_ROOT,
++ };
++
++ static const struct option options[] = {
++ { "help", no_argument, NULL, 'h' },
++ { "version", no_argument, NULL, ARG_VERSION },
++ {}
++ };
++
++ int c;
++
++ assert(argc >= 0);
++ assert(argv);
++
++ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
++ switch (c) {
++
++ case 'h':
++ help();
++ return 0;
++
++ case ARG_VERSION:
++ version();
++ return 0;
++
++ case '?':
++ return -EINVAL;
++
++ default:
++ assert_not_reached();
++ }
++
++ if (optind < argc) {
++ log_error("Extraneous arguments");
++ return -EINVAL;
++ }
++
++ return 1;
++}
++
++int main(int argc, char *argv[]) {
++ _cleanup_(manager_freep) Manager *m = NULL;
++ int fd = -1;
++ int r, n;
++
++ log_set_target(LOG_TARGET_AUTO);
++ log_parse_environment();
++ log_open();
++ init_gettext();
++
++ r = parse_argv(argc, argv);
++ if (r <= 0)
++ goto finish;
++
++ n = sd_listen_fds(0);
++ if (n > 1) {
++ log_error("Too many file descriptors received.");
++ r = -EINVAL;
++ goto finish;
++ } else if (n == 1)
++ fd = SD_LISTEN_FDS_START + 0;
++ else {
++ fd = make_socket_fd(LOG_DEBUG, FSCKD_SOCKET_PATH, SOCK_STREAM, SOCK_CLOEXEC);
++ if (fd < 0) {
++ r = log_error_errno(fd, "Couldn't create listening socket fd on %s: %m", FSCKD_SOCKET_PATH);
++ goto finish;
++ }
++ }
++
++ r = manager_new(&m, fd);
++ if (r < 0) {
++ log_error_errno(r, "Failed to allocate manager: %m");
++ goto finish;
++ }
++
++ r = run_event_loop_with_timeout(m, IDLE_TIME_SECONDS * USEC_PER_SEC);
++ if (r < 0) {
++ log_error_errno(r, "Failed to run event loop: %m");
++ goto finish;
++ }
++
++ sd_event_get_exit_code(m->event, &r);
++
++finish:
++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
++}
+diff --git a/src/fsckd/meson.build b/src/fsckd/meson.build
+new file mode 100644
+index 0000000..6d3c125
+--- /dev/null
++++ b/src/fsckd/meson.build
+@@ -0,0 +1,8 @@
++# SPDX-License-Identifier: LGPL-2.1-or-later
++
++executables += [
++ libexec_template + {
++ 'name' : 'systemd-fsckd',
++ 'sources' : files('fsckd.c'),
++ },
++]
+diff --git a/units/meson.build b/units/meson.build
+index e7bfb7f..0aee573 100644
+--- a/units/meson.build
++++ b/units/meson.build
+@@ -288,6 +288,8 @@ units = [
+ },
+ { 'file' : 'systemd-fsck-root.service.in' },
+ { 'file' : 'systemd-fsck@.service.in' },
++ { 'file' : 'systemd-fsckd.socket' },
++ { 'file' : 'systemd-fsckd.service.in' },
+ { 'file' : 'systemd-growfs-root.service.in' },
+ { 'file' : 'systemd-growfs@.service.in' },
+ { 'file' : 'systemd-halt.service' },
+diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in
+index ebe8262..ca9c7ce 100644
+--- a/units/systemd-fsck-root.service.in
++++ b/units/systemd-fsck-root.service.in
+@@ -13,6 +13,8 @@ Documentation=man:systemd-fsck-root.service(8)
+ DefaultDependencies=no
+ Conflicts=shutdown.target
+ Before=local-fs.target shutdown.target
++Wants=systemd-fsckd.socket
++After=systemd-fsckd.socket
+ ConditionPathIsReadWrite=!/
+ OnFailure=emergency.target
+ OnFailureJobMode=replace-irreversibly
+diff --git a/units/systemd-fsck@.service.in b/units/systemd-fsck@.service.in
+index 65521b1..cd8bac1 100644
+--- a/units/systemd-fsck@.service.in
++++ b/units/systemd-fsck@.service.in
+@@ -13,7 +13,8 @@ Documentation=man:systemd-fsck@.service(8)
+ DefaultDependencies=no
+ BindsTo=%i.device
+ Conflicts=shutdown.target
+-After=%i.device systemd-fsck-root.service local-fs-pre.target
++Wants=systemd-fsckd.socket
++After=%i.device systemd-fsck-root.service local-fs-pre.target systemd-fsckd.socket
+ Before=systemd-quotacheck.service shutdown.target
+
+ [Service]
+diff --git a/units/systemd-fsckd.service.in b/units/systemd-fsckd.service.in
+new file mode 100644
+index 0000000..845788c
+--- /dev/null
++++ b/units/systemd-fsckd.service.in
+@@ -0,0 +1,17 @@
++# This file is part of systemd.
++#
++# systemd is free software; you can redistribute it and/or modify it
++# under the terms of the GNU Lesser General Public License as published by
++# the Free Software Foundation; either version 2.1 of the License, or
++# (at your option) any later version.
++
++[Unit]
++Description=File System Check Daemon to report status
++Documentation=man:systemd-fsckd.service(8)
++DefaultDependencies=no
++Requires=systemd-fsckd.socket
++Before=shutdown.target
++
++[Service]
++ExecStart={{LIBEXECDIR}}/systemd-fsckd
++StandardOutput=journal+console
+diff --git a/units/systemd-fsckd.socket b/units/systemd-fsckd.socket
+new file mode 100644
+index 0000000..61fec97
+--- /dev/null
++++ b/units/systemd-fsckd.socket
+@@ -0,0 +1,15 @@
++# 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=fsck to fsckd communication Socket
++Documentation=man:systemd-fsckd.service(8) man:systemd-fsck@.service(8) man:systemd-fsck-root.service(8)
++DefaultDependencies=no
++
++[Socket]
++ListenStream=/run/systemd/fsck.progress
++SocketMode=0600
diff --git a/debian/patches/debian/localectl-disable-keymap-support.patch b/debian/patches/debian/localectl-disable-keymap-support.patch
new file mode 100644
index 0000000..50fad4c
--- /dev/null
+++ b/debian/patches/debian/localectl-disable-keymap-support.patch
@@ -0,0 +1,76 @@
+From: Luca Boccassi <bluca@debian.org>
+Date: Wed, 8 Feb 2023 20:34:38 +0000
+Subject: localectl-disable-keymap-support
+
+We no longer support old debianisms such as /etc/default/keyboard,
+so disable the keymap interface in localectl until a definitive
+solution is found.
+
+Update the test suite to skip tests for unsupported localectl features.
+---
+ src/locale/localectl.c | 11 ++++++-----
+ test/units/testsuite-73.sh | 6 ++++++
+ 2 files changed, 12 insertions(+), 5 deletions(-)
+
+diff --git a/src/locale/localectl.c b/src/locale/localectl.c
+index 3235402..c4fb1f2 100644
+--- a/src/locale/localectl.c
++++ b/src/locale/localectl.c
+@@ -383,6 +383,10 @@ static int list_x11_keymaps(int argc, char **argv, void *userdata) {
+ return 0;
+ }
+
++static int not_supported(int argc, char **argv, void *userdata) {
++ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Setting X11 and console keymaps is not supported in Debian.");
++}
++
+ static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+@@ -397,10 +401,7 @@ static int help(void) {
+ " status Show current locale settings\n"
+ " set-locale LOCALE... Set system locale\n"
+ " list-locales Show known locales\n"
+- " set-keymap MAP [MAP] Set console and X11 keyboard mappings\n"
+ " list-keymaps Show known virtual console keyboard mappings\n"
+- " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n"
+- " Set X11 and console keyboard mappings\n"
+ " list-x11-keymap-models Show known X11 keyboard mapping models\n"
+ " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n"
+ " list-x11-keymap-variants [LAYOUT]\n"
+@@ -500,9 +501,9 @@ static int localectl_main(sd_bus *bus, int argc, char *argv[]) {
+ { "status", VERB_ANY, 1, VERB_DEFAULT, show_status },
+ { "set-locale", 2, VERB_ANY, 0, set_locale },
+ { "list-locales", VERB_ANY, 1, 0, list_locales },
+- { "set-keymap", 2, 3, 0, set_vconsole_keymap },
++ { "set-keymap", 2, 3, 0, not_supported },
+ { "list-keymaps", VERB_ANY, 1, 0, list_vconsole_keymaps },
+- { "set-x11-keymap", 2, 5, 0, set_x11_keymap },
++ { "set-x11-keymap", 2, 5, 0, not_supported },
+ { "list-x11-keymap-models", VERB_ANY, 1, 0, list_x11_keymaps },
+ { "list-x11-keymap-layouts", VERB_ANY, 1, 0, list_x11_keymaps },
+ { "list-x11-keymap-variants", VERB_ANY, 2, 0, list_x11_keymaps },
+diff --git a/test/units/testsuite-73.sh b/test/units/testsuite-73.sh
+index df5af4b..ec3fa6c 100755
+--- a/test/units/testsuite-73.sh
++++ b/test/units/testsuite-73.sh
+@@ -227,6 +227,9 @@ wait_vconsole_setup() {
+ testcase_vc_keymap() {
+ local i output vc
+
++ echo "Setting X11 and console keymaps is not supported in Debian, skipping test."
++ return
++
+ if [[ -z "$(localectl list-keymaps)" ]]; then
+ echo "No vconsole keymap installed, skipping test."
+ return
+@@ -297,6 +300,9 @@ testcase_vc_keymap() {
+ testcase_x11_keymap() {
+ local output
+
++ echo "Setting X11 and console keymaps is not supported in Debian, skipping test."
++ return
++
+ if [[ -z "$(localectl list-x11-keymap-layouts)" ]]; then
+ echo "No x11 keymap installed, skipping test."
+ return
diff --git a/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch b/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
new file mode 100644
index 0000000..902612d
--- /dev/null
+++ b/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
@@ -0,0 +1,34 @@
+From: Ioanna Alifieraki <ioanna-maria.alifieraki@canonical.com>
+Date: Thu, 17 Dec 2020 14:52:07 +0000
+Subject: systemctl: do not shutdown immediately on scheduled shutdown
+
+When, for whatever reason, a scheduled shutdown fails to be set, systemd
+will proceed with immediate shutdown without allowing the user to react.
+This is counterintuitive because when a scheduled shutdown is issued,
+it means the user wants to shutdown at a specified time in the future,
+not immediately. This patch prevents the immediate shutdown and informs
+the user that no action will be taken.
+
+Fixes: #17575
+---
+ src/systemctl/systemctl-compat-halt.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c
+index 4f6e304..520e794 100644
+--- a/src/systemctl/systemctl-compat-halt.c
++++ b/src/systemctl/systemctl-compat-halt.c
+@@ -155,9 +155,11 @@ int halt_main(void) {
+
+ if (arg_force == 0) {
+ /* always try logind first */
+- if (arg_when > 0)
++ if (arg_when > 0) {
+ r = logind_schedule_shutdown(arg_action);
+- else {
++ if (r < 0)
++ return r;
++ } else {
+ r = logind_check_inhibitors(arg_action);
+ if (r < 0)
+ return r;
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..145e173
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,9 @@
+debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch
+debian/Make-run-lock-tmpfs-an-API-fs.patch
+debian/fsckd-daemon-for-inter-fsckd-communication.patch
+debian/Skip-filesystem-check-if-already-done-by-the-initram.patch
+debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch
+debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch
+debian/Downgrade-a-couple-of-warnings-to-debug.patch
+debian/Skip-flaky-test_resolved_domain_restricted_dns-in-network.patch
+debian/localectl-disable-keymap-support.patch