diff options
Diffstat (limited to 'debian/patches')
67 files changed, 7236 insertions, 0 deletions
diff --git a/debian/patches/Add-helper-for-case-independent-string-equality-checks.patch b/debian/patches/Add-helper-for-case-independent-string-equality-checks.patch new file mode 100644 index 0000000..8e69db9 --- /dev/null +++ b/debian/patches/Add-helper-for-case-independent-string-equality-checks.patch @@ -0,0 +1,24 @@ +From: Matthias Klumpp <matthias@tenstral.net> +Date: Sat, 10 Oct 2020 04:40:23 +0200 +Subject: Add helper for case-independent string equality checks + +(cherry picked from commit bd47b0dac4a1ff6e686c99b9958693e86d44007b) +--- + src/basic/string-util.h | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/basic/string-util.h b/src/basic/string-util.h +index fdd3ce7..6c99335 100644 +--- a/src/basic/string-util.h ++++ b/src/basic/string-util.h +@@ -33,6 +33,10 @@ static inline bool streq_ptr(const char *a, const char *b) { + return strcmp_ptr(a, b) == 0; + } + ++static inline bool strcaseeq_ptr(const char *a, const char *b) { ++ return strcasecmp_ptr(a, b) == 0; ++} ++ + static inline char* strstr_ptr(const char *haystack, const char *needle) { + if (!haystack || !needle) + return NULL; diff --git a/debian/patches/Always-free-deserialized_subscribed-on-reload.patch b/debian/patches/Always-free-deserialized_subscribed-on-reload.patch new file mode 100644 index 0000000..f0f6129 --- /dev/null +++ b/debian/patches/Always-free-deserialized_subscribed-on-reload.patch @@ -0,0 +1,25 @@ +From: Ali Abdallah <ali.abdallah@suse.com> +Date: Thu, 21 Jan 2021 07:37:21 +0100 +Subject: Always free deserialized_subscribed on reload + +Otherwise, it will keep consuming memory on systemctl daemon-reload. + +(cherry picked from commit 3deed59afdc2c18ecb76fe90b9bba0cd66045dfa) +--- + src/core/manager.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/core/manager.c b/src/core/manager.c +index a1d6f7c..6858950 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -3842,6 +3842,9 @@ int manager_reload(Manager *m) { + /* Clean up runtime objects no longer referenced */ + manager_vacuum(m); + ++ /* Clean up deserialized tracked clients */ ++ m->deserialized_subscribed = strv_free(m->deserialized_subscribed); ++ + /* Consider the reload process complete now. */ + assert(m->n_reloading > 0); + m->n_reloading--; diff --git a/debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch b/debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch new file mode 100644 index 0000000..83a6f2c --- /dev/null +++ b/debian/patches/Drop-bundled-copy-of-linux-if_arp.h.patch @@ -0,0 +1,219 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Wed, 15 Sep 2021 16:33:05 +0200 +Subject: Drop bundled copy of linux/if_arp.h +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +As far as I can see, we use this to get a list of ARPHRD_* defines (used in +particular for Type= in .link files). If we drop our copy, and build against +old kernel headers, the user will have a shorter list of types available. This +seems OK, and I don't think it's worth carrying our own version of this file +just to have newest possible entries. + +7c5b9952c4f6e2b72f90edbe439982528b7cf223 recently updated this file, but we'd +have to update it every time the kernel adds new entries. But if we look at +the failure carefully: + +src/basic/arphrd-from-name.gperf:65:16: error: ‘ARPHRD_MCTP’ undeclared (first use in this function); did you mean ‘ARPHRD_FCPP’? + 65 | MCTP, ARPHRD_MCTP + | ^~ + | ARPHRD_FCPP + +we see that the list we were generating was from the system headers, so it was +only as good as the system headers anyway, without the newer entries in our +bundled copy, if there were any. So let's make things simpler by always using +system headers. + +And if somebody wants to fix things so that we always have the newest list, +then we should just generate and store the converted list, not the full header. + +(cherry picked from commit e7f46ee3ae1cc66a94b293957721d68dc09d7449) +--- + src/basic/linux/if_arp.h | 164 ----------------------------------------------- + src/basic/meson.build | 1 - + 2 files changed, 165 deletions(-) + delete mode 100644 src/basic/linux/if_arp.h + +diff --git a/src/basic/linux/if_arp.h b/src/basic/linux/if_arp.h +deleted file mode 100644 +index c3cc5a9..0000000 +--- a/src/basic/linux/if_arp.h ++++ /dev/null +@@ -1,164 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +-/* +- * INET An implementation of the TCP/IP protocol suite for the LINUX +- * operating system. INET is implemented using the BSD Socket +- * interface as the means of communication with the user level. +- * +- * Global definitions for the ARP (RFC 826) protocol. +- * +- * Version: @(#)if_arp.h 1.0.1 04/16/93 +- * +- * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 +- * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. +- * Ross Biro +- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> +- * Florian La Roche, +- * Jonathan Layes <layes@loran.com> +- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> ARPHRD_HWX25 +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version +- * 2 of the License, or (at your option) any later version. +- */ +-#ifndef _UAPI_LINUX_IF_ARP_H +-#define _UAPI_LINUX_IF_ARP_H +- +-#include <linux/netdevice.h> +- +-/* ARP protocol HARDWARE identifiers. */ +-#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +-#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +-#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +-#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +-#define ARPHRD_PRONET 4 /* PROnet token ring */ +-#define ARPHRD_CHAOS 5 /* Chaosnet */ +-#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +-#define ARPHRD_ARCNET 7 /* ARCnet */ +-#define ARPHRD_APPLETLK 8 /* APPLEtalk */ +-#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +-#define ARPHRD_ATM 19 /* ATM */ +-#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ +-#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ +-#define ARPHRD_EUI64 27 /* EUI-64 */ +-#define ARPHRD_INFINIBAND 32 /* InfiniBand */ +- +-/* Dummy types for non ARP hardware */ +-#define ARPHRD_SLIP 256 +-#define ARPHRD_CSLIP 257 +-#define ARPHRD_SLIP6 258 +-#define ARPHRD_CSLIP6 259 +-#define ARPHRD_RSRVD 260 /* Notional KISS type */ +-#define ARPHRD_ADAPT 264 +-#define ARPHRD_ROSE 270 +-#define ARPHRD_X25 271 /* CCITT X.25 */ +-#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */ +-#define ARPHRD_CAN 280 /* Controller Area Network */ +-#define ARPHRD_PPP 512 +-#define ARPHRD_CISCO 513 /* Cisco HDLC */ +-#define ARPHRD_HDLC ARPHRD_CISCO +-#define ARPHRD_LAPB 516 /* LAPB */ +-#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */ +-#define ARPHRD_RAWHDLC 518 /* Raw HDLC */ +-#define ARPHRD_RAWIP 519 /* Raw IP */ +- +-#define ARPHRD_TUNNEL 768 /* IPIP tunnel */ +-#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ +-#define ARPHRD_FRAD 770 /* Frame Relay Access Device */ +-#define ARPHRD_SKIP 771 /* SKIP vif */ +-#define ARPHRD_LOOPBACK 772 /* Loopback device */ +-#define ARPHRD_LOCALTLK 773 /* Localtalk device */ +-#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */ +-#define ARPHRD_BIF 775 /* AP1000 BIF */ +-#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */ +-#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */ +-#define ARPHRD_IPGRE 778 /* GRE over IP */ +-#define ARPHRD_PIMREG 779 /* PIMSM register interface */ +-#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */ +-#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */ +-#define ARPHRD_ECONET 782 /* Acorn Econet */ +-#define ARPHRD_IRDA 783 /* Linux-IrDA */ +-/* ARP works differently on different FC media .. so */ +-#define ARPHRD_FCPP 784 /* Point to point fibrechannel */ +-#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */ +-#define ARPHRD_FCPL 786 /* Fibrechannel public loop */ +-#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */ +- /* 787->799 reserved for fibrechannel media types */ +-#define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR */ +-#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */ +-#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ +-#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ +-#define ARPHRD_IEEE802154 804 +-#define ARPHRD_IEEE802154_MONITOR 805 /* IEEE 802.15.4 network monitor */ +- +-#define ARPHRD_PHONET 820 /* PhoNet media type */ +-#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */ +-#define ARPHRD_CAIF 822 /* CAIF media type */ +-#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */ +-#define ARPHRD_NETLINK 824 /* Netlink header */ +-#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */ +-#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */ +- +-#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ +-#define ARPHRD_NONE 0xFFFE /* zero header length */ +- +-/* ARP protocol opcodes. */ +-#define ARPOP_REQUEST 1 /* ARP request */ +-#define ARPOP_REPLY 2 /* ARP reply */ +-#define ARPOP_RREQUEST 3 /* RARP request */ +-#define ARPOP_RREPLY 4 /* RARP reply */ +-#define ARPOP_InREQUEST 8 /* InARP request */ +-#define ARPOP_InREPLY 9 /* InARP reply */ +-#define ARPOP_NAK 10 /* (ATM)ARP NAK */ +- +- +-/* ARP ioctl request. */ +-struct arpreq { +- struct sockaddr arp_pa; /* protocol address */ +- struct sockaddr arp_ha; /* hardware address */ +- int arp_flags; /* flags */ +- struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ +- char arp_dev[IFNAMSIZ]; +-}; +- +-struct arpreq_old { +- struct sockaddr arp_pa; /* protocol address */ +- struct sockaddr arp_ha; /* hardware address */ +- int arp_flags; /* flags */ +- struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ +-}; +- +-/* ARP Flag values. */ +-#define ATF_COM 0x02 /* completed entry (ha valid) */ +-#define ATF_PERM 0x04 /* permanent entry */ +-#define ATF_PUBL 0x08 /* publish entry */ +-#define ATF_USETRAILERS 0x10 /* has requested trailers */ +-#define ATF_NETMASK 0x20 /* want to use a netmask (only +- for proxy entries) */ +-#define ATF_DONTPUB 0x40 /* don't answer this addresses */ +- +-/* +- * This structure defines an ethernet arp header. +- */ +- +-struct arphdr { +- __be16 ar_hrd; /* format of hardware address */ +- __be16 ar_pro; /* format of protocol address */ +- unsigned char ar_hln; /* length of hardware address */ +- unsigned char ar_pln; /* length of protocol address */ +- __be16 ar_op; /* ARP opcode (command) */ +- +-#if 0 +- /* +- * Ethernet looks like this : This bit is variable sized however... +- */ +- unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ +- unsigned char ar_sip[4]; /* sender IP address */ +- unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ +- unsigned char ar_tip[4]; /* target IP address */ +-#endif +- +-}; +- +- +-#endif /* _UAPI_LINUX_IF_ARP_H */ +diff --git a/src/basic/meson.build b/src/basic/meson.build +index 1183ea8..2c13cf4 100644 +--- a/src/basic/meson.build ++++ b/src/basic/meson.build +@@ -98,7 +98,6 @@ basic_sources = files(''' + linux/hdlc/ioctl.h + linux/if.h + linux/if_addr.h +- linux/if_arp.h + linux/if_bonding.h + linux/if_bridge.h + linux/if_ether.h diff --git a/debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch b/debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch new file mode 100644 index 0000000..c9e3500 --- /dev/null +++ b/debian/patches/LoadCredentials-do-not-assert-on-invalid-syntax.patch @@ -0,0 +1,34 @@ +From: Luca Boccassi <luca.boccassi@microsoft.com> +Date: Thu, 1 Apr 2021 22:18:29 +0100 +Subject: LoadCredentials: do not assert on invalid syntax + +LoadCredentials=foo causes an assertion to be triggered, as we +are not checking that the rvalue's right hand side part is non-empty +before using it in unit_full_printf. + +Fixes #19178 + +# printf [Service]nLoadCredential=passwd.hashed-password.rootn > hello.service +# systemd-analyze verify ./hello.service +... +Assertion 'format' failed at src/core/unit-printf.c:232, function unit_full_printf(). Aborting. +Aborted (core dumped) + +(cherry picked from commit f7a6f1226e800f7695c2073675523062ea697aa4) +--- + src/core/load-fragment.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 4964249..5b66fb1 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -4569,7 +4569,7 @@ int config_parse_load_credential( + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) + return log_oom(); +- if (r <= 0) { ++ if (r <= 0 || isempty(p)) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + return 0; + } diff --git a/debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch b/debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch new file mode 100644 index 0000000..fa14d0d --- /dev/null +++ b/debian/patches/Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch @@ -0,0 +1,52 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Wed, 10 Mar 2021 10:17:23 +0100 +Subject: Revert "udev: do not execute hwdb builtin import twice or thrice" + +This reverts commit 876c75fe870846b09b54423a6b719d80bc879b27. + +The patch seems to cause usb devices to get some attributes set from the parent +PCI device. 'hwdb' builtin has support for breaking iteration upwards on usb +devices. But when '--subsystem=foo' is specified, iteration is continued. I'm +sure it *could* be figured out, but it seems hard to get all the combinations +correct. So let's revert to functional status quo ante, even if does the lookup +more than once unnecessarily. + +Fixes #18125. + +(cherry picked from commit 451ba55fecd8b494add2001b3ca3c1915c8fd655) +--- + rules.d/50-udev-default.rules.in | 3 +-- + rules.d/60-serial.rules | 5 ++--- + 2 files changed, 3 insertions(+), 5 deletions(-) + +diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in +index cef78f9..50747a1 100644 +--- a/rules.d/50-udev-default.rules.in ++++ b/rules.d/50-udev-default.rules.in +@@ -10,9 +10,8 @@ SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio- + SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" + SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100" + +-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb", GOTO="default_hwdb_imported" ++SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" + ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}" +-LABEL="default_hwdb_imported" + + ACTION!="add", GOTO="default_end" + +diff --git a/rules.d/60-serial.rules b/rules.d/60-serial.rules +index b162665..f303e27 100644 +--- a/rules.d/60-serial.rules ++++ b/rules.d/60-serial.rules +@@ -4,9 +4,8 @@ ACTION=="remove", GOTO="serial_end" + SUBSYSTEM!="tty", GOTO="serial_end" + + SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +-# We already ran the hwdb builtin for devices with MODALIAS in 50-default.rules. +-# Let's cover the remaining case here, where we walk up the tree to find a node with $MODALIAS. +-ENV{MODALIAS}=="", SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" ++SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" ++SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" + + # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices + KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" diff --git a/debian/patches/analyze-slightly-reword-PrivateTmp-message.patch b/debian/patches/analyze-slightly-reword-PrivateTmp-message.patch new file mode 100644 index 0000000..e6d9f1a --- /dev/null +++ b/debian/patches/analyze-slightly-reword-PrivateTmp-message.patch @@ -0,0 +1,26 @@ +From: Lennart Poettering <lennart@poettering.net> +Date: Wed, 10 Feb 2021 10:50:23 +0100 +Subject: analyze: slightly reword PrivateTmp= message + +Apparently there way confusion about "does not apply". Let's say "is not +appropriate". + +Fixes: #13095 +(cherry picked from commit 77552b9520ba0d47cbf33cdbe1ddedb9ce9b5bf3) +--- + src/analyze/analyze-security.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c +index 8d94fbc..99ec7b5 100644 +--- a/src/analyze/analyze-security.c ++++ b/src/analyze/analyze-security.c +@@ -1545,7 +1545,7 @@ static int assess(const struct security_info *info, Table *overview_table, Analy + + if (a->default_dependencies_only && !info->default_dependencies) { + badness = UINT64_MAX; +- d = strdup("Service runs in special boot phase, option does not apply"); ++ d = strdup("Service runs in special boot phase, option is not appropriate"); + if (!d) + return log_oom(); + } else { diff --git a/debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch b/debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch new file mode 100644 index 0000000..de1b1f2 --- /dev/null +++ b/debian/patches/ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch @@ -0,0 +1,37 @@ +From: Aleksey Vasenev <margtu-fivt@ya.ru> +Date: Wed, 5 Oct 2022 22:33:53 +0300 +Subject: ata_id: Fixed getting Response Code from SCSI Sense Data (#24921) + +The Response Code is contained in the first byte of the SCSI Sense Data. +Bit number 7 is reserved or has a different meaning for some Response Codes +and is set to 1 for some drives. + +(cherry picked from commit 2be1ae54badf7a3a12908a8094ebaba8f91887ca) +--- + src/udev/ata_id/ata_id.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c +index ce0bf5d..c86e40b 100644 +--- a/src/udev/ata_id/ata_id.c ++++ b/src/udev/ata_id/ata_id.c +@@ -162,8 +162,8 @@ static int disk_identify_command( + return ret; + } + +- if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c) && +- !(sense[0] == 0x70 && sense[12] == 0x00 && sense[13] == 0x1d)) { ++ if (!((sense[0] & 0x7f) == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c) && ++ !((sense[0] & 0x7f) == 0x70 && sense[12] == 0x00 && sense[13] == 0x1d)) { + errno = EIO; + return -1; + } +@@ -240,7 +240,7 @@ static int disk_identify_packet_device_command( + return ret; + } + +- if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { ++ if (!((sense[0] & 0x7f) == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { + errno = EIO; + return -1; + } diff --git a/debian/patches/basic-add-make_mount_point_inode-helper.patch b/debian/patches/basic-add-make_mount_point_inode-helper.patch new file mode 100644 index 0000000..49207c3 --- /dev/null +++ b/debian/patches/basic-add-make_mount_point_inode-helper.patch @@ -0,0 +1,239 @@ +From: Luca Boccassi <bluca@debian.org> +Date: Sat, 19 Dec 2020 21:40:47 +0000 +Subject: basic: add make_mount_point_inode helper + +Creates a file or a directory depending on the source path, useful +for creating mount points. + +(cherry picked from commit 8bab8029105e44ce78c5e11bffa203a1135fe201) +--- + src/basic/mountpoint-util.c | 25 +++++++++++++++++++++ + src/basic/mountpoint-util.h | 4 ++++ + src/core/namespace.c | 26 +++++++-------------- + src/machine/machine-dbus.c | 14 ++++-------- + src/test/test-mountpoint-util.c | 50 +++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 91 insertions(+), 28 deletions(-) + +diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c +index a6602ad..ed7457f 100644 +--- a/src/basic/mountpoint-util.c ++++ b/src/basic/mountpoint-util.c +@@ -8,14 +8,17 @@ + #include "fd-util.h" + #include "fileio.h" + #include "fs-util.h" ++#include "label.h" + #include "missing_stat.h" + #include "missing_syscall.h" ++#include "mkdir.h" + #include "mountpoint-util.h" + #include "parse-util.h" + #include "path-util.h" + #include "stat-util.h" + #include "stdio-util.h" + #include "strv.h" ++#include "user-util.h" + + /* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of + * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code +@@ -509,3 +512,25 @@ int mount_propagation_flags_from_string(const char *name, unsigned long *ret) { + return -EINVAL; + return 0; + } ++ ++int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode) { ++ assert(st); ++ assert(dest); ++ ++ if (S_ISDIR(st->st_mode)) ++ return mkdir_label(dest, mode); ++ else ++ return mknod(dest, S_IFREG|(mode & ~0111), 0); ++} ++ ++int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode) { ++ struct stat st; ++ ++ assert(source); ++ assert(dest); ++ ++ if (stat(source, &st) < 0) ++ return -errno; ++ ++ return make_mount_point_inode_from_stat(&st, dest, mode); ++} +diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h +index aadb212..cebcec5 100644 +--- a/src/basic/mountpoint-util.h ++++ b/src/basic/mountpoint-util.h +@@ -23,3 +23,7 @@ int dev_is_devtmpfs(void); + + const char *mount_propagation_flags_to_string(unsigned long flags); + int mount_propagation_flags_from_string(const char *name, unsigned long *ret); ++ ++/* Creates a mount point (not parents) based on the source path or stat - ie, a file or a directory */ ++int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode); ++int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode); +diff --git a/src/core/namespace.c b/src/core/namespace.c +index cdf427a..02381da 100644 +--- a/src/core/namespace.c ++++ b/src/core/namespace.c +@@ -1176,29 +1176,19 @@ static int apply_mount( + bool try_again = false; + + if (r == -ENOENT && make) { +- struct stat st; ++ int q; + + /* Hmm, either the source or the destination are missing. Let's see if we can create + the destination, then try again. */ + +- if (stat(what, &st) < 0) +- log_error_errno(errno, "Mount point source '%s' is not accessible: %m", what); +- else { +- int q; ++ (void) mkdir_parents(mount_entry_path(m), 0755); + +- (void) mkdir_parents(mount_entry_path(m), 0755); +- +- if (S_ISDIR(st.st_mode)) +- q = mkdir(mount_entry_path(m), 0755) < 0 ? -errno : 0; +- else +- q = touch(mount_entry_path(m)); +- +- if (q < 0) +- log_error_errno(q, "Failed to create destination mount point node '%s': %m", +- mount_entry_path(m)); +- else +- try_again = true; +- } ++ q = make_mount_point_inode_from_path(what, mount_entry_path(m), 0755); ++ if (q < 0) ++ log_error_errno(q, "Failed to create destination mount point node '%s': %m", ++ mount_entry_path(m)); ++ else ++ try_again = true; + } + + if (try_again) +diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c +index bb67beb..1105008 100644 +--- a/src/machine/machine-dbus.c ++++ b/src/machine/machine-dbus.c +@@ -32,6 +32,7 @@ + #include "missing_capability.h" + #include "mkdir.h" + #include "mount-util.h" ++#include "mountpoint-util.h" + #include "namespace-util.h" + #include "os-util.h" + #include "path-util.h" +@@ -908,10 +909,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu + + /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */ + mount_tmp = strjoina(mount_slave, "/mount"); +- if (S_ISDIR(st.st_mode)) +- r = mkdir_errno_wrapper(mount_tmp, 0700); +- else +- r = touch(mount_tmp); ++ r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700); + if (r < 0) { + sd_bus_error_set_errnof(error, r, "Failed to create temporary mount point %s: %m", mount_tmp); + goto finish; +@@ -1003,12 +1001,8 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu + } + + if (make_file_or_directory) { +- if (S_ISDIR(st.st_mode)) +- (void) mkdir_p(dest, 0755); +- else { +- (void) mkdir_parents(dest, 0755); +- (void) mknod(dest, S_IFREG|0600, 0); +- } ++ (void) mkdir_parents(dest, 0755); ++ (void) make_mount_point_inode_from_stat(&st, dest, 0700); + } + + mount_inside = strjoina("/run/host/incoming/", basename(mount_outside)); +diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c +index 47fde5c..1ce3d5d 100644 +--- a/src/test/test-mountpoint-util.c ++++ b/src/test/test-mountpoint-util.c +@@ -8,13 +8,16 @@ + #include "def.h" + #include "fd-util.h" + #include "fileio.h" ++#include "fs-util.h" + #include "hashmap.h" + #include "log.h" ++#include "mkdir.h" + #include "mountpoint-util.h" + #include "path-util.h" + #include "rm-rf.h" + #include "string-util.h" + #include "tests.h" ++#include "tmpfile-util.h" + + static void test_mount_propagation_flags(const char *name, int ret, unsigned long expected) { + long unsigned flags; +@@ -287,6 +290,52 @@ static void test_fd_is_mount_point(void) { + assert_se(IN_SET(fd_is_mount_point(fd, "root/", 0), -ENOENT, 0)); + } + ++static void test_make_mount_point_inode(void) { ++ _cleanup_(rm_rf_physical_and_freep) char *d = NULL; ++ const char *src_file, *src_dir, *dst_file, *dst_dir; ++ struct stat st; ++ ++ log_info("/* %s */", __func__); ++ ++ assert_se(mkdtemp_malloc(NULL, &d) >= 0); ++ ++ src_file = strjoina(d, "/src/file"); ++ src_dir = strjoina(d, "/src/dir"); ++ dst_file = strjoina(d, "/dst/file"); ++ dst_dir = strjoina(d, "/dst/dir"); ++ ++ assert_se(mkdir_p(src_dir, 0755) >= 0); ++ assert_se(mkdir_parents(dst_file, 0755) >= 0); ++ assert_se(touch(src_file) >= 0); ++ ++ assert_se(make_mount_point_inode_from_path(src_file, dst_file, 0755) >= 0); ++ assert_se(make_mount_point_inode_from_path(src_dir, dst_dir, 0755) >= 0); ++ ++ assert_se(stat(dst_dir, &st) == 0); ++ assert_se(S_ISDIR(st.st_mode)); ++ assert_se(stat(dst_file, &st) == 0); ++ assert_se(S_ISREG(st.st_mode)); ++ assert_se(!(S_IXUSR & st.st_mode)); ++ assert_se(!(S_IXGRP & st.st_mode)); ++ assert_se(!(S_IXOTH & st.st_mode)); ++ ++ assert_se(unlink(dst_file) == 0); ++ assert_se(rmdir(dst_dir) == 0); ++ ++ assert_se(stat(src_file, &st) == 0); ++ assert_se(make_mount_point_inode_from_stat(&st, dst_file, 0755) >= 0); ++ assert_se(stat(src_dir, &st) == 0); ++ assert_se(make_mount_point_inode_from_stat(&st, dst_dir, 0755) >= 0); ++ ++ assert_se(stat(dst_dir, &st) == 0); ++ assert_se(S_ISDIR(st.st_mode)); ++ assert_se(stat(dst_file, &st) == 0); ++ assert_se(S_ISREG(st.st_mode)); ++ assert_se(!(S_IXUSR & st.st_mode)); ++ assert_se(!(S_IXGRP & st.st_mode)); ++ assert_se(!(S_IXOTH & st.st_mode)); ++} ++ + int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + +@@ -311,6 +360,7 @@ int main(int argc, char *argv[]) { + test_mnt_id(); + test_path_is_mount_point(); + test_fd_is_mount_point(); ++ test_make_mount_point_inode(); + + return 0; + } diff --git a/debian/patches/basic-unit-name-adjust-comments.patch b/debian/patches/basic-unit-name-adjust-comments.patch new file mode 100644 index 0000000..d83b1d7 --- /dev/null +++ b/debian/patches/basic-unit-name-adjust-comments.patch @@ -0,0 +1,36 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Wed, 23 Jun 2021 11:52:56 +0200 +Subject: basic/unit-name: adjust comments +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +We already checked for "too long" right above… + +(cherry picked from commit 4e2544c30bfb95e7cb4d1551ba066b1a56520ad6) +--- + src/basic/unit-name.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c +index 9b6cacd..e286831 100644 +--- a/src/basic/unit-name.c ++++ b/src/basic/unit-name.c +@@ -528,7 +528,7 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) { + if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */ + return -ENAMETOOLONG; + +- /* Refuse this if this got too long or for some other reason didn't result in a valid name */ ++ /* Refuse if this for some other reason didn't result in a valid name */ + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return -EINVAL; + +@@ -562,7 +562,7 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha + if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */ + return -ENAMETOOLONG; + +- /* Refuse this if this got too long or for some other reason didn't result in a valid name */ ++ /* Refuse if this for some other reason didn't result in a valid name */ + if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE)) + return -EINVAL; + diff --git a/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch b/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch new file mode 100644 index 0000000..b080d25 --- /dev/null +++ b/debian/patches/basic-unit-name-do-not-use-strdupa-on-a-path.patch @@ -0,0 +1,65 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Wed, 23 Jun 2021 11:46:41 +0200 +Subject: basic/unit-name: do not use strdupa() on a path + +The path may have unbounded length, for example through a fuse mount. + +CVE-2021-33910: attacked controlled alloca() leads to crash in systemd and +ultimately a kernel panic. Systemd parses the content of /proc/self/mountinfo +and each mountpoint is passed to mount_setup_unit(), which calls +unit_name_path_escape() underneath. A local attacker who is able to mount a +filesystem with a very long path can crash systemd and the whole system. + +https://bugzilla.redhat.com/show_bug.cgi?id=1970887 + +The resulting string length is bounded by UNIT_NAME_MAX, which is 256. But we +can't easily check the length after simplification before doing the +simplification, which in turns uses a copy of the string we can write to. +So we can't reject paths that are too long before doing the duplication. +Hence the most obvious solution is to switch back to strdup(), as before +7410616cd9dbbec97cf98d75324da5cda2b2f7a2. + +(cherry picked from commit 441e0115646d54f080e5c3bb0ba477c892861ab9) +(cherry picked from commit 764b74113e36ac5219a4b82a05f311b5a92136ce) +(cherry picked from commit 4a1c5f34bd3e1daed4490e9d97918e504d19733b) +(cherry picked from commit b00674347337b7531c92fdb65590ab253bb57538) +--- + src/basic/unit-name.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c +index 5f595af..9b6cacd 100644 +--- a/src/basic/unit-name.c ++++ b/src/basic/unit-name.c +@@ -378,12 +378,13 @@ int unit_name_unescape(const char *f, char **ret) { + } + + int unit_name_path_escape(const char *f, char **ret) { +- char *p, *s; ++ _cleanup_free_ char *p = NULL; ++ char *s; + + assert(f); + assert(ret); + +- p = strdupa(f); ++ p = strdup(f); + if (!p) + return -ENOMEM; + +@@ -395,13 +396,9 @@ int unit_name_path_escape(const char *f, char **ret) { + if (!path_is_normalized(p)) + return -EINVAL; + +- /* Truncate trailing slashes */ ++ /* Truncate trailing slashes and skip leading slashes */ + delete_trailing_chars(p, "/"); +- +- /* Truncate leading slashes */ +- p = skip_leading_chars(p, "/"); +- +- s = unit_name_escape(p); ++ s = unit_name_escape(skip_leading_chars(p, "/")); + } + if (!s) + return -ENOMEM; diff --git a/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch b/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch new file mode 100644 index 0000000..0dffcf3 --- /dev/null +++ b/debian/patches/btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch @@ -0,0 +1,106 @@ +From: Lennart Poettering <lennart@poettering.net> +Date: Fri, 26 Feb 2021 17:39:55 +0100 +Subject: btrfs-util: add helper that abstracts "might be btrfs subvol?" check + +Let#s not hardcode inode nr 256 everywhere, but abstract this check +slightly. + +(cherry picked from commit 674b04ff1b6deab17f5d36c036c0275ba94e1ebc) +(cherry picked from commit 190c6bcfc3518bec964ab740085ac88ccc86dcc7) +--- + src/basic/btrfs-util.c | 6 +++--- + src/basic/btrfs-util.h | 10 ++++++++++ + src/basic/rm-rf.c | 2 +- + src/import/export-tar.c | 2 +- + src/shared/machine-image.c | 3 +-- + 5 files changed, 16 insertions(+), 7 deletions(-) + +diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c +index 2634659..f0df51a 100644 +--- a/src/basic/btrfs-util.c ++++ b/src/basic/btrfs-util.c +@@ -91,7 +91,7 @@ int btrfs_is_subvol_fd(int fd) { + if (fstat(fd, &st) < 0) + return -errno; + +- if (!S_ISDIR(st.st_mode) || st.st_ino != 256) ++ if (!btrfs_might_be_subvol(&st)) + return 0; + + return btrfs_is_filesystem(fd); +@@ -194,7 +194,7 @@ int btrfs_subvol_set_read_only_fd(int fd, bool b) { + if (fstat(fd, &st) < 0) + return -errno; + +- if (!S_ISDIR(st.st_mode) || st.st_ino != 256) ++ if (!btrfs_might_be_subvol(&st)) + return -EINVAL; + + if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) +@@ -229,7 +229,7 @@ int btrfs_subvol_get_read_only_fd(int fd) { + if (fstat(fd, &st) < 0) + return -errno; + +- if (!S_ISDIR(st.st_mode) || st.st_ino != 256) ++ if (!btrfs_might_be_subvol(&st)) + return -EINVAL; + + if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) +diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h +index c8b44f6..0f569b6 100644 +--- a/src/basic/btrfs-util.h ++++ b/src/basic/btrfs-util.h +@@ -127,3 +127,13 @@ static inline int btrfs_log_dev_root(int level, int ret, const char *p) { + "File system behind %s is reported by btrfs to be backed by pseudo-device /dev/root, which is not a valid userspace accessible device node. " + "Cannot determine correct backing block device.", p); + } ++ ++static inline bool btrfs_might_be_subvol(const struct stat *st) { ++ if (!st) ++ return false; ++ ++ /* Returns true if this 'struct stat' looks like it could refer to a btrfs subvolume. To make a final ++ * decision, needs to be combined with an fstatfs() check to see if this is actually btrfs. */ ++ ++ return S_ISDIR(st->st_mode) && st->st_ino == 256; ++} +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index b0d682f..4c39ce8 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -147,7 +147,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { + if (r > 0) + continue; + +- if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { ++ if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) { + + /* This could be a subvolume, try to remove it */ + +diff --git a/src/import/export-tar.c b/src/import/export-tar.c +index b8b650f..1e6b2c1 100644 +--- a/src/import/export-tar.c ++++ b/src/import/export-tar.c +@@ -283,7 +283,7 @@ int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType + + e->quota_referenced = (uint64_t) -1; + +- if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */ ++ if (btrfs_might_be_subvol(&e->st)) { + BtrfsQuotaInfo q; + + r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q); +diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c +index 671a56b..c7cf5e9 100644 +--- a/src/shared/machine-image.c ++++ b/src/shared/machine-image.c +@@ -248,8 +248,7 @@ static int image_make( + if (fd < 0) + return -errno; + +- /* btrfs subvolumes have inode 256 */ +- if (st->st_ino == 256) { ++ if (btrfs_might_be_subvol(st)) { + + r = btrfs_is_filesystem(fd); + if (r < 0) diff --git a/debian/patches/core-fix-mtime-calculation-of-dropin-files.patch b/debian/patches/core-fix-mtime-calculation-of-dropin-files.patch new file mode 100644 index 0000000..4c6b1e8 --- /dev/null +++ b/debian/patches/core-fix-mtime-calculation-of-dropin-files.patch @@ -0,0 +1,100 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Thu, 4 Mar 2021 00:36:24 +0100 +Subject: core: fix mtime calculation of dropin files + +Nominally, the bug was in unit_load_dropin(), which just took the last mtime +instead of calculating the maximum. But instead of adding code to wrap the +loop, this patch goes in the other direction. + +All (correct) callers of config_parse() followed a very similar pattern to +calculate the maximum mtime. So let's simplify things by making config_parse() +assume that mtime is initialized and update it to the maximum. This makes all +the callers that care about mtime simpler and also fixes the issue in +unit_load_dropin(). + +config_parse_many_nulstr() and config_parse_many() are different, because it +makes sense to call them just once, and current ret_mtime behaviour make sense. + +Fixes #17730, https://bugzilla.redhat.com/show_bug.cgi?id=1933137. + +(cherry picked from commit da46a1bc3cd28ac36114002c216196dae004b05c) +--- + src/core/load-dropin.c | 1 + + src/shared/conf-parser.c | 15 +++++++-------- + src/shared/conf-parser.h | 2 +- + 3 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c +index d1c85e2..3bb4856 100644 +--- a/src/core/load-dropin.c ++++ b/src/core/load-dropin.c +@@ -112,6 +112,7 @@ int unit_load_dropin(Unit *u) { + return log_oom(); + } + ++ u->dropin_mtime = 0; + STRV_FOREACH(f, u->dropin_paths) + (void) config_parse( + u->id, *f, NULL, +diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c +index 35d301d..099c47a 100644 +--- a/src/shared/conf-parser.c ++++ b/src/shared/conf-parser.c +@@ -259,7 +259,7 @@ int config_parse(const char *unit, + const void *table, + ConfigParseFlags flags, + void *userdata, +- usec_t *ret_mtime) { ++ usec_t *latest_mtime) { + + _cleanup_free_ char *section = NULL, *continuation = NULL; + _cleanup_fclose_ FILE *ours = NULL; +@@ -271,6 +271,9 @@ int config_parse(const char *unit, + assert(filename); + assert(lookup); + ++ /* latest_mtime is an input-output parameter: it will be updated if the mtime of the file we're ++ * looking at is later than the current *latest_mtime value. */ ++ + if (!f) { + f = ours = fopen(filename, "re"); + if (!f) { +@@ -413,8 +416,8 @@ int config_parse(const char *unit, + } + } + +- if (ret_mtime) +- *ret_mtime = mtime; ++ if (latest_mtime) ++ *latest_mtime = MAX(*latest_mtime, mtime); + + return 0; + } +@@ -440,13 +443,9 @@ static int config_parse_many_files( + } + + STRV_FOREACH(fn, files) { +- usec_t t; +- +- r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &t); ++ r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &mtime); + if (r < 0) + return r; +- if (t > mtime) /* Find the newest */ +- mtime = t; + } + + if (ret_mtime) +diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h +index f115cb2..84c9bf6 100644 +--- a/src/shared/conf-parser.h ++++ b/src/shared/conf-parser.h +@@ -89,7 +89,7 @@ int config_parse( + const void *table, + ConfigParseFlags flags, + void *userdata, +- usec_t *ret_mtime); /* possibly NULL */ ++ usec_t *latest_mtime); /* input/output, possibly NULL */ + + int config_parse_many_nulstr( + const char *conf_file, /* possibly NULL */ diff --git a/debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch b/debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch new file mode 100644 index 0000000..f1029d3 --- /dev/null +++ b/debian/patches/coredump-do-not-allow-user-to-access-coredumps-with-chang.patch @@ -0,0 +1,388 @@ +From: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> +Date: Mon, 28 Nov 2022 12:12:55 +0100 +Subject: coredump: do not allow user to access coredumps with changed + uid/gid/capabilities + +When the user starts a program which elevates its permissions via setuid, +setgid, or capabilities set on the file, it may access additional information +which would then be visible in the coredump. We shouldn't make the the coredump +visible to the user in such cases. + +Reported-by: Matthias Gerstner <mgerstner@suse.de> + +This reads the /proc/<pid>/auxv file and attaches it to the process metadata as +PROC_AUXV. Before the coredump is submitted, it is parsed and if either +at_secure was set (which the kernel will do for processes that are setuid, +setgid, or setcap), or if the effective uid/gid don't match uid/gid, the file +is not made accessible to the user. If we can't access this data, we assume the +file should not be made accessible either. In principle we could also access +the auxv data from a note in the core file, but that is much more complex and +it seems better to use the stand-alone file that is provided by the kernel. + +Attaching auxv is both convient for this patch (because this way it's passed +between the stages along with other fields), but I think it makes sense to save +it in general. + +We use the information early in the core file to figure out if the program was +32-bit or 64-bit and its endianness. This way we don't need heuristics to guess +whether the format of the auxv structure. This test might reject some cases on +fringe architecutes. But the impact would be limited: we just won't grant the +user permissions to view the coredump file. If people report that we're missing +some cases, we can always enhance this to support more architectures. + +I tested auxv parsing on amd64, 32-bit program on amd64, arm64, arm32, and +ppc64el, but not the whole coredump handling. + +(cherry picked from commit 3e4d0f6cf99f8677edd6a237382a65bfe758de03) +(cherry picked from commit 9b75a3d0502d6741c8ecb7175794345f8eb3827c) +(cherry picked from commit efca5283dc791a07171f80eef84e14fdb58fad57) +(cherry picked from commit 1d5e0e9910500f3c3584485f77bfc35e601036e3) +(cherry picked from commit 8215e1527d859e77dd1378fd7e42bbd32130edb3) +(cherry picked from commit 786df410b1cb3a2294c9a5d118c958525e7439e6) +--- + src/basic/io-util.h | 9 +++ + src/coredump/coredump.c | 200 ++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 194 insertions(+), 15 deletions(-) + +diff --git a/src/basic/io-util.h b/src/basic/io-util.h +index d817714..dacec71 100644 +--- a/src/basic/io-util.h ++++ b/src/basic/io-util.h +@@ -85,7 +85,16 @@ struct iovec_wrapper *iovw_new(void); + struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw); + struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw); + void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors); ++ + int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len); ++static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) { ++ /* Move data into iovw or free on error */ ++ int r = iovw_put(iovw, data, len); ++ if (r < 0) ++ free(data); ++ return r; ++} ++ + int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value); + int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value); + void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); +diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c +index 0a1cb91..b60dff3 100644 +--- a/src/coredump/coredump.c ++++ b/src/coredump/coredump.c +@@ -3,6 +3,7 @@ + #include <errno.h> + #include <stdio.h> + #include <sys/prctl.h> ++#include <sys/auxv.h> + #include <sys/xattr.h> + #include <unistd.h> + +@@ -96,6 +97,7 @@ enum { + + META_EXE = _META_MANDATORY_MAX, + META_UNIT, ++ META_PROC_AUXV, + _META_MAX + }; + +@@ -110,10 +112,12 @@ static const char * const meta_field_names[_META_MAX] = { + [META_COMM] = "COREDUMP_COMM=", + [META_EXE] = "COREDUMP_EXE=", + [META_UNIT] = "COREDUMP_UNIT=", ++ [META_PROC_AUXV] = "COREDUMP_PROC_AUXV=", + }; + + typedef struct Context { + const char *meta[_META_MAX]; ++ size_t meta_size[_META_MAX]; + pid_t pid; + bool is_pid1; + bool is_journald; +@@ -175,14 +179,17 @@ static uint64_t storage_size_max(void) { + return 0; + } + +-static int fix_acl(int fd, uid_t uid) { +- +-#if HAVE_ACL +- int r; +- ++static int fix_acl(int fd, uid_t uid, bool allow_user) { + assert(fd >= 0); + assert(uid_is_valid(uid)); + ++#if HAVE_ACL ++ int r; ++ ++ /* We don't allow users to read coredumps if the uid or capabilities were changed. */ ++ if (!allow_user) ++ return 0; ++ + if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) + return 0; + +@@ -242,7 +249,8 @@ static int fix_permissions( + const char *filename, + const char *target, + const Context *context, +- uid_t uid) { ++ uid_t uid, ++ bool allow_user) { + + int r; + +@@ -252,7 +260,7 @@ static int fix_permissions( + + /* Ignore errors on these */ + (void) fchmod(fd, 0640); +- (void) fix_acl(fd, uid); ++ (void) fix_acl(fd, uid, allow_user); + (void) fix_xattr(fd, context); + + if (fsync(fd) < 0) +@@ -323,6 +331,153 @@ static int make_filename(const Context *context, char **ret) { + return 0; + } + ++static int parse_auxv64( ++ const uint64_t *auxv, ++ size_t size_bytes, ++ int *at_secure, ++ uid_t *uid, ++ uid_t *euid, ++ gid_t *gid, ++ gid_t *egid) { ++ ++ assert(auxv || size_bytes == 0); ++ ++ if (size_bytes % (2 * sizeof(uint64_t)) != 0) ++ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes); ++ ++ size_t words = size_bytes / sizeof(uint64_t); ++ ++ /* Note that we set output variables even on error. */ ++ ++ for (size_t i = 0; i + 1 < words; i += 2) ++ switch (auxv[i]) { ++ case AT_SECURE: ++ *at_secure = auxv[i + 1] != 0; ++ break; ++ case AT_UID: ++ *uid = auxv[i + 1]; ++ break; ++ case AT_EUID: ++ *euid = auxv[i + 1]; ++ break; ++ case AT_GID: ++ *gid = auxv[i + 1]; ++ break; ++ case AT_EGID: ++ *egid = auxv[i + 1]; ++ break; ++ case AT_NULL: ++ if (auxv[i + 1] != 0) ++ goto error; ++ return 0; ++ } ++ error: ++ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), ++ "AT_NULL terminator not found, cannot parse auxv structure."); ++} ++ ++static int parse_auxv32( ++ const uint32_t *auxv, ++ size_t size_bytes, ++ int *at_secure, ++ uid_t *uid, ++ uid_t *euid, ++ gid_t *gid, ++ gid_t *egid) { ++ ++ assert(auxv || size_bytes == 0); ++ ++ size_t words = size_bytes / sizeof(uint32_t); ++ ++ if (size_bytes % (2 * sizeof(uint32_t)) != 0) ++ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes); ++ ++ /* Note that we set output variables even on error. */ ++ ++ for (size_t i = 0; i + 1 < words; i += 2) ++ switch (auxv[i]) { ++ case AT_SECURE: ++ *at_secure = auxv[i + 1] != 0; ++ break; ++ case AT_UID: ++ *uid = auxv[i + 1]; ++ break; ++ case AT_EUID: ++ *euid = auxv[i + 1]; ++ break; ++ case AT_GID: ++ *gid = auxv[i + 1]; ++ break; ++ case AT_EGID: ++ *egid = auxv[i + 1]; ++ break; ++ case AT_NULL: ++ if (auxv[i + 1] != 0) ++ goto error; ++ return 0; ++ } ++ error: ++ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), ++ "AT_NULL terminator not found, cannot parse auxv structure."); ++} ++ ++static int grant_user_access(int core_fd, const Context *context) { ++ int at_secure = -1; ++ uid_t uid = UID_INVALID, euid = UID_INVALID; ++ uid_t gid = GID_INVALID, egid = GID_INVALID; ++ int r; ++ ++ assert(core_fd >= 0); ++ assert(context); ++ ++ if (!context->meta[META_PROC_AUXV]) ++ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), "No auxv data, not adjusting permissions."); ++ ++ uint8_t elf[EI_NIDENT]; ++ errno = 0; ++ if (pread(core_fd, &elf, sizeof(elf), 0) != sizeof(elf)) ++ return log_warning_errno(errno_or_else(EIO), ++ "Failed to pread from coredump fd: %s", errno != 0 ? strerror_safe(errno) : "Unexpected EOF"); ++ ++ if (elf[EI_MAG0] != ELFMAG0 || ++ elf[EI_MAG1] != ELFMAG1 || ++ elf[EI_MAG2] != ELFMAG2 || ++ elf[EI_MAG3] != ELFMAG3 || ++ elf[EI_VERSION] != EV_CURRENT) ++ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), ++ "Core file does not have ELF header, not adjusting permissions."); ++ if (!IN_SET(elf[EI_CLASS], ELFCLASS32, ELFCLASS64) || ++ !IN_SET(elf[EI_DATA], ELFDATA2LSB, ELFDATA2MSB)) ++ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), ++ "Core file has strange ELF class, not adjusting permissions."); ++ ++ if ((elf[EI_DATA] == ELFDATA2LSB) != (__BYTE_ORDER == __LITTLE_ENDIAN)) ++ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), ++ "Core file has non-native endianness, not adjusting permissions."); ++ ++ if (elf[EI_CLASS] == ELFCLASS64) ++ r = parse_auxv64((const uint64_t*) context->meta[META_PROC_AUXV], ++ context->meta_size[META_PROC_AUXV], ++ &at_secure, &uid, &euid, &gid, &egid); ++ else ++ r = parse_auxv32((const uint32_t*) context->meta[META_PROC_AUXV], ++ context->meta_size[META_PROC_AUXV], ++ &at_secure, &uid, &euid, &gid, &egid); ++ if (r < 0) ++ return r; ++ ++ /* We allow access if we got all the data and at_secure is not set and ++ * the uid/gid matches euid/egid. */ ++ bool ret = ++ at_secure == 0 && ++ uid != UID_INVALID && euid != UID_INVALID && uid == euid && ++ gid != GID_INVALID && egid != GID_INVALID && gid == egid; ++ log_debug("Will %s access (uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)", ++ ret ? "permit" : "restrict", ++ uid, euid, gid, egid, yes_no(at_secure)); ++ return ret; ++} ++ + static int save_external_coredump( + const Context *context, + int input_fd, +@@ -403,6 +558,8 @@ static int save_external_coredump( + goto fail; + } + ++ bool allow_user = grant_user_access(fd, context) > 0; ++ + #if HAVE_COMPRESSION + /* If we will remove the coredump anyway, do not compress. */ + if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) { +@@ -428,7 +585,7 @@ static int save_external_coredump( + goto fail_compressed; + } + +- r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid); ++ r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid, allow_user); + if (r < 0) + goto fail_compressed; + +@@ -451,7 +608,7 @@ static int save_external_coredump( + uncompressed: + #endif + +- r = fix_permissions(fd, tmp, fn, context, uid); ++ r = fix_permissions(fd, tmp, fn, context, uid, allow_user); + if (r < 0) + goto fail; + +@@ -700,7 +857,7 @@ static int change_uid_gid(const Context *context) { + } + + static int submit_coredump( +- Context *context, ++ const Context *context, + struct iovec_wrapper *iovw, + int input_fd) { + +@@ -822,16 +979,15 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) { + struct iovec *iovec = iovw->iovec + n; + + for (i = 0; i < ELEMENTSOF(meta_field_names); i++) { +- char *p; +- + /* Note that these strings are NUL terminated, because we made sure that a + * trailing NUL byte is in the buffer, though not included in the iov_len + * count (see process_socket() and gather_pid_metadata_*()) */ + assert(((char*) iovec->iov_base)[iovec->iov_len] == 0); + +- p = startswith(iovec->iov_base, meta_field_names[i]); ++ const char *p = startswith(iovec->iov_base, meta_field_names[i]); + if (p) { + context->meta[i] = p; ++ context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]); + count++; + break; + } +@@ -1074,6 +1230,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) { + uid_t owner_uid; + pid_t pid; + char *t; ++ size_t size; + const char *p; + int r; + +@@ -1139,13 +1296,26 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) { + (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_LIMITS=", t); + + p = procfs_file_alloca(pid, "cgroup"); +- if (read_full_file(p, &t, NULL) >=0) ++ if (read_full_file(p, &t, NULL) >= 0) + (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_CGROUP=", t); + + p = procfs_file_alloca(pid, "mountinfo"); +- if (read_full_file(p, &t, NULL) >=0) ++ if (read_full_file(p, &t, NULL) >= 0) + (void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_MOUNTINFO=", t); + ++ /* We attach /proc/auxv here. ELF coredumps also contain a note for this (NT_AUXV), see elf(5). */ ++ p = procfs_file_alloca(pid, "auxv"); ++ if (read_full_virtual_file(p, &t, &size) >= 0) { ++ char *buf = malloc(strlen("COREDUMP_PROC_AUXV=") + size + 1); ++ if (buf) { ++ /* Add a dummy terminator to make save_context() happy. */ ++ *((uint8_t*) mempcpy(stpcpy(buf, "COREDUMP_PROC_AUXV="), t, size)) = '\0'; ++ (void) iovw_consume(iovw, buf, size + strlen("COREDUMP_PROC_AUXV=")); ++ } ++ ++ free(t); ++ } ++ + if (get_process_cwd(pid, &t) >= 0) + (void) iovw_put_string_field_free(iovw, "COREDUMP_CWD=", t); + diff --git a/debian/patches/debian/Add-env-variable-for-machine-ID-path.patch b/debian/patches/debian/Add-env-variable-for-machine-ID-path.patch new file mode 100644 index 0000000..7645d97 --- /dev/null +++ b/debian/patches/debian/Add-env-variable-for-machine-ID-path.patch @@ -0,0 +1,77 @@ +From: Martin Pitt <mpitt@debian.org> +Date: Wed, 18 Jan 2017 11:21:35 +0100 +Subject: Add env variable for machine ID path + +During package build, in minimal chroots, or other systems which do not already +have an /etc/machine-id we get six test failures. Introduce a +$SYSTEMD_MACHINE_ID_PATH environment variable which can specify a location +other than /etc/machine-id, so that the unit tests are independent from the +environment. + +Also adjust test-fs-util to not assume that /etc/machine-id exists. Use +/etc/passwd instead which is created by base-files. + +Closes: #851445 + +Bug: https://bugs.freedesktop.org/show_bug.cgi?id=62344 +--- + src/libsystemd/sd-id128/sd-id128.c | 2 +- + src/test/test-fs-util.c | 11 +++++++---- + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c +index d5de935..78612a0 100644 +--- a/src/libsystemd/sd-id128/sd-id128.c ++++ b/src/libsystemd/sd-id128/sd-id128.c +@@ -88,7 +88,7 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { + assert_return(ret, -EINVAL); + + if (sd_id128_is_null(saved_machine_id)) { +- r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id); ++ r = id128_read(getenv("SYSTEMD_MACHINE_ID_PATH") ?: "/etc/machine-id", ID128_PLAIN, &saved_machine_id); + if (r < 0) + return r; + +diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c +index d1f9252..4c93adb 100644 +--- a/src/test/test-fs-util.c ++++ b/src/test/test-fs-util.c +@@ -211,7 +211,7 @@ static void test_chase_symlinks(void) { + assert_se(streq(result, "/test-chase.fsldajfl")); + result = mfree(result); + +- r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result, NULL); ++ r = chase_symlinks("/etc/passwd/foo", NULL, 0, &result, NULL); + assert_se(r == -ENOTDIR); + result = mfree(result); + +@@ -284,23 +284,26 @@ static void test_chase_symlinks(void) { + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL, NULL) >= 0); + } + +- p = strjoina(temp, "/machine-id-test"); +- assert_se(symlink("/usr/../etc/./machine-id", p) >= 0); ++ p = strjoina(temp, "/passwd-test"); ++ assert_se(symlink("/usr/../etc/./passwd", p) >= 0); + + r = chase_symlinks(p, NULL, 0, NULL, &pfd); + if (r != -ENOENT) { + _cleanup_close_ int fd = -1; ++/* + sd_id128_t a, b; ++*/ + + assert_se(pfd >= 0); + + fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC); + assert_se(fd >= 0); + safe_close(pfd); +- ++/* + assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0); + assert_se(sd_id128_get_machine(&b) >= 0); + assert_se(sd_id128_equal(a, b)); ++*/ + } + + /* Test CHASE_NOFOLLOW */ diff --git a/debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch b/debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch new file mode 100644 index 0000000..226dea2 --- /dev/null +++ b/debian/patches/debian/Add-support-for-TuxOnIce-hibernation.patch @@ -0,0 +1,30 @@ +From: Julien Muchembled <jm@jmuchemb.eu> +Date: Tue, 29 Apr 2014 11:40:50 +0200 +Subject: Add support for TuxOnIce hibernation + +systemd does not support non-mainline kernel features so upstream rejected this +patch. +It is however required for systemd integration by tuxonice-userui package. + +Forwarded: http://lists.freedesktop.org/archives/systemd-devel/2014-April/018960.html +--- + src/shared/sleep-config.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c +index cea5148..66e1675 100644 +--- a/src/shared/sleep-config.c ++++ b/src/shared/sleep-config.c +@@ -471,6 +471,12 @@ static bool enough_swap_for_hibernation(void) { + if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) + return true; + ++ /* TuxOnIce is an alternate implementation for hibernation. ++ * It can be configured to compress the image to a file or an inactive ++ * swap partition, so there's nothing more we can do here. */ ++ if (access("/sys/power/tuxonice", F_OK) == 0) ++ return true; ++ + r = find_hibernate_location(&hibernate_location); + if (r < 0) + return false; 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/Don-t-enable-audit-by-default.patch b/debian/patches/debian/Don-t-enable-audit-by-default.patch new file mode 100644 index 0000000..b4cf902 --- /dev/null +++ b/debian/patches/debian/Don-t-enable-audit-by-default.patch @@ -0,0 +1,53 @@ +From: Martin Pitt <martin.pitt@ubuntu.com> +Date: Sun, 28 Dec 2014 12:49:35 +0100 +Subject: Don't enable audit by default + +It causes flooding of dmesg and syslog, suppressing actually important +messages. + +Don't enable it for now, until a better solution is found: +http://lists.freedesktop.org/archives/systemd-devel/2014-December/026591.html + +Bug-Debian: https://bugs.debian.org/773528 +--- + man/journald.conf.xml | 2 +- + src/journal/journald-server.c | 2 +- + src/journal/journald.conf | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/man/journald.conf.xml b/man/journald.conf.xml +index 2134a1d..a4a42a4 100644 +--- a/man/journald.conf.xml ++++ b/man/journald.conf.xml +@@ -417,7 +417,7 @@ + <command>systemd-journald</command> collects generated audit records, it just controls whether it + tells the kernel to generate them. This means if another tool turns on auditing even if + <command>systemd-journald</command> left it off, it will still collect the generated +- messages. Defaults to on.</para></listitem> ++ messages. Defaults to off.</para></listitem> + </varlistentry> + + <varlistentry> +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index ce15766..bd7fa8f 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -2188,7 +2188,7 @@ int server_init(Server *s, const char *namespace) { + .compress.threshold_bytes = (uint64_t) -1, + .seal = true, + +- .set_audit = true, ++ .set_audit = false, + + .watchdog_usec = USEC_INFINITY, + +diff --git a/src/journal/journald.conf b/src/journal/journald.conf +index d6cd5b1..5e4b0e2 100644 +--- a/src/journal/journald.conf ++++ b/src/journal/journald.conf +@@ -41,4 +41,4 @@ + #MaxLevelWall=emerg + #LineMax=48K + #ReadKMsg=yes +-#Audit=yes ++#Audit=no 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..1b5b03d --- /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 5b66fb1..df5669a 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -372,6 +372,7 @@ static int patch_var_run( + + const char *e; + char *z; ++ int log_level; + + e = path_startswith(*path, "/var/run/"); + if (!e) +@@ -381,7 +382,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 008a825..ab0054e 100644 +--- a/src/sysv-generator/sysv-generator.c ++++ b/src/sysv-generator/sysv-generator.c +@@ -787,7 +787,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { + if (!fpath) + return log_oom(); + +- log_warning("SysV service '%s' lacks a native systemd unit file. " ++ log_debug("SysV service '%s' lacks a native systemd unit file. " + "Automatically generating a unit file for compatibility. " + "Please update package to include a native systemd unit file, in order to make it more safe and robust.", fpath); + +diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c +index 9906c70..31e5707 100644 +--- a/src/tmpfiles/tmpfiles.c ++++ b/src/tmpfiles/tmpfiles.c +@@ -2538,6 +2538,7 @@ static int specifier_expansion_from_arg(Item *i) { + static int patch_var_run(const char *fname, unsigned line, char **path) { + const char *k; + char *n; ++ int log_level; + + assert(path); + assert(*path); +@@ -2563,7 +2564,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, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n); ++ 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); + + free_and_replace(*path, n); + diff --git a/debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch b/debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch new file mode 100644 index 0000000..4ce4884 --- /dev/null +++ b/debian/patches/debian/Drop-seccomp-system-call-filter-for-udev.patch @@ -0,0 +1,31 @@ +From: Michael Biebl <biebl@debian.org> +Date: Wed, 18 Jul 2018 23:49:16 +0200 +Subject: Drop seccomp system call filter for udev + +The seccomp based system call whitelist requires at least systemd 239 to +be the active init and during a dist-upgrade we can't guarantee that +systemd has been fully configured before udev is restarted. + +This partially reverts upstream commit +ee8f26180d01e3ddd4e5f20b03b81e5e737657ae. + +Once buster is released, this patch can be dropped. + +Closes: #903224 +--- + units/systemd-udevd.service.in | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in +index f3458d9..225eac2 100644 +--- a/units/systemd-udevd.service.in ++++ b/units/systemd-udevd.service.in +@@ -35,8 +35,6 @@ MemoryDenyWriteExecute=yes + RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 + RestrictRealtime=yes + RestrictSUIDSGID=yes +-SystemCallFilter=@system-service @module @raw-io +-SystemCallErrorNumber=EPERM + SystemCallArchitectures=native + LockPersonality=yes + IPAddressDeny=any diff --git a/debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch b/debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch new file mode 100644 index 0000000..3e9aa5a --- /dev/null +++ b/debian/patches/debian/Keep-journal-files-compatible-with-older-versions.patch @@ -0,0 +1,69 @@ +From: Michael Biebl <biebl@debian.org> +Date: Mon, 17 Aug 2020 22:11:19 +0200 +Subject: Keep journal files compatible with older versions + +Disable the KEYED-HASH journal feature by default and keep LZ4 (instead +of ZSTD) as default compression for new journal files. Otherwise journal +files are incompatible and can't be read by older journalctl +implementations. + +This patch can be dropped in bullseye+1, as journalctl from bullseye +will then be able to read journal files with those features. + +Closes: #968055 +--- + src/journal/compress.h | 10 +++++----- + src/journal/journal-file.c | 8 ++++---- + 2 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/src/journal/compress.h b/src/journal/compress.h +index db7f399..6cd9290 100644 +--- a/src/journal/compress.h ++++ b/src/journal/compress.h +@@ -18,14 +18,14 @@ int compress_blob_zstd(const void *src, uint64_t src_size, + static inline int compress_blob(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { + int r; +-#if HAVE_ZSTD +- r = compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size); +- if (r == 0) +- return OBJECT_COMPRESSED_ZSTD; +-#elif HAVE_LZ4 ++#if HAVE_LZ4 + r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size); + if (r == 0) + return OBJECT_COMPRESSED_LZ4; ++#elif HAVE_ZSTD ++ r = compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size); ++ if (r == 0) ++ return OBJECT_COMPRESSED_ZSTD; + #elif HAVE_XZ + r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size); + if (r == 0) +diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c +index 15336be..6ce18c9 100644 +--- a/src/journal/journal-file.c ++++ b/src/journal/journal-file.c +@@ -3411,10 +3411,10 @@ int journal_file_open( + .prot = prot_from_flags(flags), + .writable = (flags & O_ACCMODE) != O_RDONLY, + +-#if HAVE_ZSTD +- .compress_zstd = compress, +-#elif HAVE_LZ4 ++#if HAVE_LZ4 + .compress_lz4 = compress, ++#elif HAVE_ZSTD ++ .compress_zstd = compress, + #elif HAVE_XZ + .compress_xz = compress, + #endif +@@ -3432,7 +3432,7 @@ int journal_file_open( + if (r < 0) { + if (r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring."); +- f->keyed_hash = true; ++ f->keyed_hash = false; + } else + f->keyed_hash = r; + diff --git a/debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch b/debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch new file mode 100644 index 0000000..cf8db56 --- /dev/null +++ b/debian/patches/debian/Let-graphical-session-pre.target-be-manually-started.patch @@ -0,0 +1,22 @@ +From: Iain Lane <iain@orangesquash.org.uk> +Date: Mon, 22 Aug 2016 07:03:27 +0200 +Subject: Let graphical-session-pre.target be manually started + +This is needed until https://github.com/systemd/systemd/issues/3750 is fixed. + +Forwarded: not-needed +Bug-Ubuntu: https://launchpad.net/bugs/1615341 +--- + units/user/graphical-session-pre.target | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/units/user/graphical-session-pre.target b/units/user/graphical-session-pre.target +index 4b9e3dc..dffaf85 100644 +--- a/units/user/graphical-session-pre.target ++++ b/units/user/graphical-session-pre.target +@@ -12,5 +12,4 @@ Description=Session services which should run early before the graphical session + Documentation=man:systemd.special(7) + Requires=basic.target + Before=graphical-session.target +-RefuseManualStart=yes + StopWhenUnneeded=yes 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..a596b7a --- /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/core/mount-setup.c | 2 ++ + tmpfiles.d/legacy.conf | 1 - + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c +index 915b101..7a330dd 100644 +--- a/src/core/mount-setup.c ++++ b/src/core/mount-setup.c +@@ -86,6 +86,8 @@ static const MountPoint mount_table[] = { + #endif + { "tmpfs", "/run", "tmpfs", "mode=755" 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, + cg_is_unified_wanted, 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 b/tmpfiles.d/legacy.conf +index 62e2ae0..ea5e735 100644 +--- a/tmpfiles.d/legacy.conf ++++ b/tmpfiles.d/legacy.conf +@@ -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 + + # /run/lock/subsys is used for serializing SysV service execution, and diff --git a/debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch b/debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch new file mode 100644 index 0000000..ae0bb3f --- /dev/null +++ b/debian/patches/debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch @@ -0,0 +1,68 @@ +From: Michael Biebl <biebl@debian.org> +Date: Thu, 15 Oct 2020 23:11:01 +0200 +Subject: Move sysusers.d/sysctl.d/binfmt.d/modules-load.d back to /usr + +In Debian, late mounting of /usr is no longer supported, so it is safe +to install those files in /usr. +We want those facilities in /usr, not /, as this will make an eventual +switch to a merged-usr setup easier. + +Closes: #971282 +--- + src/core/systemd.pc.in | 8 ++++---- + src/libsystemd/sd-path/sd-path.c | 8 ++++---- + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in +index b5cc8f9..21dbf30 100644 +--- a/src/core/systemd.pc.in ++++ b/src/core/systemd.pc.in +@@ -65,16 +65,16 @@ systemdshutdowndir=${systemd_shutdown_dir} + tmpfiles_dir=${prefix}/lib/tmpfiles.d + tmpfilesdir=${tmpfiles_dir} + +-sysusers_dir=${rootprefix}/lib/sysusers.d ++sysusers_dir=${prefix}/lib/sysusers.d + sysusersdir=${sysusers_dir} + +-sysctl_dir=${rootprefix}/lib/sysctl.d ++sysctl_dir=${prefix}/lib/sysctl.d + sysctldir=${sysctl_dir} + +-binfmt_dir=${rootprefix}/lib/binfmt.d ++binfmt_dir=${prefix}/lib/binfmt.d + binfmtdir=${binfmt_dir} + +-modules_load_dir=${rootprefix}/lib/modules-load.d ++modules_load_dir=${prefix}/lib/modules-load.d + modulesloaddir=${modules_load_dir} + + catalog_dir=${prefix}/lib/systemd/catalog +diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c +index 61ed7cb..682e3f1 100644 +--- a/src/libsystemd/sd-path/sd-path.c ++++ b/src/libsystemd/sd-path/sd-path.c +@@ -369,19 +369,19 @@ static int get_path(uint64_t type, char **buffer, const char **ret) { + return 0; + + case SD_PATH_SYSUSERS: +- *ret = ROOTPREFIX_NOSLASH "/lib/sysusers.d"; ++ *ret = "/usr/lib/sysusers.d"; + return 0; + + case SD_PATH_SYSCTL: +- *ret = ROOTPREFIX_NOSLASH "/lib/sysctl.d"; ++ *ret = "/usr/lib/sysctl.d"; + return 0; + + case SD_PATH_BINFMT: +- *ret = ROOTPREFIX_NOSLASH "/lib/binfmt.d"; ++ *ret = "/usr/lib/binfmt.d"; + return 0; + + case SD_PATH_MODULES_LOAD: +- *ret = ROOTPREFIX_NOSLASH "/lib/modules-load.d"; ++ *ret = "/usr/lib/modules-load.d"; + return 0; + + case SD_PATH_CATALOG: diff --git a/debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch b/debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch new file mode 100644 index 0000000..4683aae --- /dev/null +++ b/debian/patches/debian/Only-start-logind-if-dbus-is-installed.patch @@ -0,0 +1,24 @@ +From: Martin Pitt <martin.pitt@ubuntu.com> +Date: Mon, 9 Feb 2015 10:53:43 +0100 +Subject: Only start logind if dbus is installed + +logind fails to start in environments without dbus, such as LXC containers or +servers. Add a startup condition to avoid the very noisy startup failure. + +Part of #772700 +--- + units/systemd-logind.service.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in +index f131b60..952a07c 100644 +--- a/units/systemd-logind.service.in ++++ b/units/systemd-logind.service.in +@@ -16,6 +16,7 @@ Documentation=man:org.freedesktop.login1(5) + + Wants=user.slice modprobe@drm.service + After=nss-user-lookup.target user.slice modprobe@drm.service ++ConditionPathExists=/lib/systemd/system/dbus.service + + # Ask for the dbus socket. + Wants=dbus.socket diff --git a/debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch b/debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch new file mode 100644 index 0000000..68b08a0 --- /dev/null +++ b/debian/patches/debian/Re-enable-journal-forwarding-to-syslog.patch @@ -0,0 +1,56 @@ +From: Martin Pitt <martin.pitt@ubuntu.com> +Date: Fri, 28 Nov 2014 14:43:25 +0100 +Subject: Re-enable journal forwarding to syslog + +Revert upstream commit 46b131574fdd7d77 for now, until Debian's sysloggers +can/do all read from the journal directly. See + + http://lists.freedesktop.org/archives/systemd-devel/2014-November/025550.html + +for details. Once we grow a journal.conf.d/ directory, sysloggers can be moved +to pulling from the journal one by one and disable forwarding again in such a +conf.d snippet. +--- + man/journald.conf.xml | 2 +- + src/journal/journald-server.c | 1 + + src/journal/journald.conf | 2 +- + 3 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/man/journald.conf.xml b/man/journald.conf.xml +index 959815a..2134a1d 100644 +--- a/man/journald.conf.xml ++++ b/man/journald.conf.xml +@@ -344,7 +344,7 @@ + traditional syslog daemon, to the kernel log buffer (kmsg), to the system console, or sent as wall + messages to all logged-in users. These options take boolean arguments. If forwarding to syslog is + enabled but nothing reads messages from the socket, forwarding to syslog has no effect. By default, +- only forwarding to wall is enabled. These settings may be overridden at boot time with the kernel ++ only forwarding to syslog and wall is enabled. These settings may be overridden at boot time with the kernel + command line options <literal>systemd.journald.forward_to_syslog</literal>, + <literal>systemd.journald.forward_to_kmsg</literal>, + <literal>systemd.journald.forward_to_console</literal>, and +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 10ebc3e..ce15766 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -2198,6 +2198,7 @@ int server_init(Server *s, const char *namespace) { + .ratelimit_interval = DEFAULT_RATE_LIMIT_INTERVAL, + .ratelimit_burst = DEFAULT_RATE_LIMIT_BURST, + ++ .forward_to_syslog = true, + .forward_to_wall = true, + + .max_file_usec = DEFAULT_MAX_FILE_USEC, +diff --git a/src/journal/journald.conf b/src/journal/journald.conf +index 2e1aacd..d6cd5b1 100644 +--- a/src/journal/journald.conf ++++ b/src/journal/journald.conf +@@ -29,7 +29,7 @@ + #RuntimeMaxFiles=100 + #MaxRetentionSec= + #MaxFileSec=1month +-#ForwardToSyslog=no ++#ForwardToSyslog=yes + #ForwardToKMsg=no + #ForwardToConsole=no + #ForwardToWall=yes diff --git a/debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch b/debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch new file mode 100644 index 0000000..3346001 --- /dev/null +++ b/debian/patches/debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch @@ -0,0 +1,37 @@ +From: Martin Pitt <martin.pitt@ubuntu.com> +Date: Mon, 27 Apr 2015 15:29:13 +0200 +Subject: Revert "core: one step back again, + for nspawn we actually can't wait for cgroups running empty since systemd + will get exactly zero notifications about it" + +This reverts commit 743970d2ea6d08aa7c7bff8220f6b7702f2b1db7. + +Bug-Debian: https://bugs.debian.org/784720 +Bug-Ubuntu: https://launchpad.net/bugs/1448259 +Bug-Fedora: https://bugzilla.redhat.com/show_bug.cgi?id=1141137 +--- + src/core/unit.c | 11 +---------- + 1 file changed, 1 insertion(+), 10 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 45a417a..811e038 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -5106,16 +5106,7 @@ int unit_kill_context( + + } else if (r > 0) { + +- /* FIXME: For now, on the legacy hierarchy, we will not wait for the cgroup members to die if +- * we are running in a container or if this is a delegation unit, simply because cgroup +- * notification is unreliable in these cases. It doesn't work at all in containers, and outside +- * of containers it can be confused easily by left-over directories in the cgroup — which +- * however should not exist in non-delegated units. On the unified hierarchy that's different, +- * there we get proper events. Hence rely on them. */ +- +- if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 || +- (detect_container() == 0 && !unit_cgroup_delegate(u))) +- wait_for_exit = true; ++ wait_for_exit = true; + + if (send_sighup) { + set_free(pid_set); diff --git a/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..7ed358b --- /dev/null +++ b/debian/patches/debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch @@ -0,0 +1,46 @@ +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 | 2 -- + sysctl.d/50-coredump.conf.in | 2 +- + 2 files changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index a280b75..5c2a73a 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -2669,8 +2669,6 @@ int main(int argc, char *argv[]) { + kernel_timestamp = DUAL_TIMESTAMP_NULL; + } + +- initialize_coredump(skip_setup); +- + r = fixup_environment(); + if (r < 0) { + log_emergency_errno(r, "Failed to fix up PID 1 environment: %m"); +diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in +index 4338756..8e501c4 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=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %h ++kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t 9223372036854775808 %h + + # Allow that 16 coredumps are dispatched in parallel by the kernel. We want to + # be able to collect process metadata from /proc/%P/ while processing diff --git a/debian/patches/debian/Revert-udev-fix-memleak.patch b/debian/patches/debian/Revert-udev-fix-memleak.patch new file mode 100644 index 0000000..ed90a10 --- /dev/null +++ b/debian/patches/debian/Revert-udev-fix-memleak.patch @@ -0,0 +1,30 @@ +From: Michael Biebl <biebl@debian.org> +Date: Sat, 25 Sep 2021 21:07:17 +0200 +Subject: Revert "udev: fix memleak" + +This reverts commit 5dd2b56443e2ed81c238094f516a622804b35518. +--- + src/udev/udev-node.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index b8b93ee..2cc78c9 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -194,7 +194,7 @@ static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, + + /* manage "stack of names" with possibly specified device priorities */ + static int link_update(sd_device *dev, const char *slink, bool add) { +- _cleanup_free_ char *filename = NULL, *dirname = NULL; ++ _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL; + char name_enc[PATH_MAX]; + const char *id_filename; + int i, r, retries; +@@ -237,7 +237,6 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1; + + for (i = 0; i < retries; i++) { +- _cleanup_free_ char *target = NULL; + struct stat st1 = {}, st2 = {}; + + r = stat(dirname, &st1); diff --git a/debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch b/debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch new file mode 100644 index 0000000..e3f1c64 --- /dev/null +++ b/debian/patches/debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch @@ -0,0 +1,47 @@ +From: Michael Biebl <biebl@debian.org> +Date: Sat, 25 Sep 2021 21:08:26 +0200 +Subject: Revert "udev: link_update() should fail if the entry in symlink dir + couldn't have been created" + +This reverts commit c07dc6cedc6e6fbc28a0da3e8c8b12900423b409. +--- + src/udev/udev-node.c | 21 +++++++++------------ + 1 file changed, 9 insertions(+), 12 deletions(-) + +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index 2cc78c9..bde18f7 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -214,23 +214,20 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + if (!filename) + return log_oom(); + +- if (!add) { +- if (unlink(filename) == 0) +- (void) rmdir(dirname); +- } else +- for (;;) { ++ if (!add && unlink(filename) == 0) ++ (void) rmdir(dirname); ++ ++ if (add) ++ do { + _cleanup_close_ int fd = -1; + + r = mkdir_parents(filename, 0755); + if (!IN_SET(r, 0, -ENOENT)) +- return r; +- +- fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); +- if (fd >= 0) + break; +- if (errno != ENOENT) +- return -errno; +- } ++ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); ++ if (fd < 0) ++ r = -errno; ++ } while (r == -ENOENT); + + /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink + * will be fixed in the second invocation. */ diff --git a/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch b/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch new file mode 100644 index 0000000..36bdbb5 --- /dev/null +++ b/debian/patches/debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch @@ -0,0 +1,163 @@ +From: Michael Biebl <biebl@debian.org> +Date: Sat, 25 Sep 2021 21:08:36 +0200 +Subject: Revert "udev: make algorithm that selects highest priority devlink + less susceptible to race conditions" + +This reverts commit 30f6dce62cb3a738b20253f2192270607c31b55b. +--- + src/udev/udev-event.c | 7 ----- + src/udev/udev-node.c | 75 +++++++++++---------------------------------------- + 2 files changed, 15 insertions(+), 67 deletions(-) + +diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c +index 5159d19..9cf5190 100644 +--- a/src/udev/udev-event.c ++++ b/src/udev/udev-event.c +@@ -1041,13 +1041,6 @@ int udev_event_execute_rules(UdevEvent *event, + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m"); + +- /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database, +- * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure +- * symlinks point to devices that claim them with the highest priority. */ +- r = update_devnode(event); +- if (r < 0) +- return r; +- + device_set_is_initialized(dev); + + return 0; +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index bde18f7..9d4b7d9 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -20,15 +20,12 @@ + #include "path-util.h" + #include "selinux-util.h" + #include "smack-util.h" +-#include "stat-util.h" + #include "stdio-util.h" + #include "string-util.h" + #include "strxcpyx.h" + #include "udev-node.h" + #include "user-util.h" + +-#define LINK_UPDATE_MAX_RETRIES 128 +- + static int node_symlink(sd_device *dev, const char *node, const char *slink) { + _cleanup_free_ char *slink_dirname = NULL, *target = NULL; + const char *id_filename, *slink_tmp; +@@ -102,9 +99,7 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) { + if (rename(slink_tmp, slink) < 0) { + r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink); + (void) unlink(slink_tmp); +- } else +- /* Tell caller that we replaced already existing symlink. */ +- r = 1; ++ } + + return r; + } +@@ -197,7 +192,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + _cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL; + char name_enc[PATH_MAX]; + const char *id_filename; +- int i, r, retries; ++ int r; + + assert(dev); + assert(slink); +@@ -217,6 +212,14 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + if (!add && unlink(filename) == 0) + (void) rmdir(dirname); + ++ r = link_find_prioritized(dev, add, dirname, &target); ++ if (r < 0) { ++ log_device_debug(dev, "No reference left, removing '%s'", slink); ++ if (unlink(slink) == 0) ++ (void) rmdir_parents(slink, "/"); ++ } else ++ (void) node_symlink(dev, target, slink); ++ + if (add) + do { + _cleanup_close_ int fd = -1; +@@ -229,49 +232,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) { + r = -errno; + } while (r == -ENOENT); + +- /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink +- * will be fixed in the second invocation. */ +- retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1; +- +- for (i = 0; i < retries; i++) { +- struct stat st1 = {}, st2 = {}; +- +- r = stat(dirname, &st1); +- if (r < 0 && errno != ENOENT) +- return -errno; +- +- r = link_find_prioritized(dev, add, dirname, &target); +- if (r == -ENOENT) { +- log_device_debug(dev, "No reference left, removing '%s'", slink); +- if (unlink(slink) == 0) +- (void) rmdir_parents(slink, "/"); +- +- break; +- } else if (r < 0) +- return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m"); +- +- r = node_symlink(dev, target, slink); +- if (r < 0) { +- (void) unlink(filename); +- break; +- } else if (r == 1) +- /* We have replaced already existing symlink, possibly there is some other device trying +- * to claim the same symlink. Let's do one more iteration to give us a chance to fix +- * the error if other device actually claims the symlink with higher priority. */ +- continue; +- +- /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */ +- if ((st1.st_mode & S_IFMT) != 0) { +- r = stat(dirname, &st2); +- if (r < 0 && errno != ENOENT) +- return -errno; +- +- if (stat_inode_unmodified(&st1, &st2)) +- break; +- } +- } +- +- return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; ++ return r; + } + + int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { +@@ -490,11 +451,8 @@ int udev_node_add(sd_device *dev, bool apply, + (void) node_symlink(dev, devnode, filename); + + /* create/update symlinks, add symlinks to name index */ +- FOREACH_DEVICE_DEVLINK(dev, devlink) { +- r = link_update(dev, devlink, true); +- if (r < 0) +- log_device_info_errno(dev, r, "Failed to update device symlinks: %m"); +- } ++ FOREACH_DEVICE_DEVLINK(dev, devlink) ++ (void) link_update(dev, devlink, true); + + return 0; + } +@@ -507,11 +465,8 @@ int udev_node_remove(sd_device *dev) { + assert(dev); + + /* remove/update symlinks, remove symlinks from name index */ +- FOREACH_DEVICE_DEVLINK(dev, devlink) { +- r = link_update(dev, devlink, false); +- if (r < 0) +- log_device_info_errno(dev, r, "Failed to update device symlinks: %m"); +- } ++ FOREACH_DEVICE_DEVLINK(dev, devlink) ++ (void) link_update(dev, devlink, false); + + r = xsprintf_dev_num_path_from_sd_device(dev, &filename); + if (r < 0) 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..659692f --- /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 15f5892..e38d9a9 100644 +--- a/src/fstab-generator/fstab-generator.c ++++ b/src/fstab-generator/fstab-generator.c +@@ -357,6 +357,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); +@@ -434,9 +435,13 @@ static int add_mount( + return r; + + 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 1dce176..908d931 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 + + [Service] + Type=oneshot diff --git a/debian/patches/debian/Use-Debian-specific-config-files.patch b/debian/patches/debian/Use-Debian-specific-config-files.patch new file mode 100644 index 0000000..0c0a82a --- /dev/null +++ b/debian/patches/debian/Use-Debian-specific-config-files.patch @@ -0,0 +1,459 @@ +From: Michael Biebl <biebl@debian.org> +Date: Thu, 18 Jul 2013 20:11:02 +0200 +Subject: Use Debian specific config files + +Use /etc/default/locale instead of /etc/locale.conf for locale settings. + +Use /etc/default/keyboard instead of /etc/X11/xorg.conf.d/00-keyboard.conf for +keyboard configuration. + +Read/write /etc/timezone if /etc/localtime does not exist. +--- + src/basic/time-util.c | 34 ++++++-- + src/core/locale-setup.c | 21 +++++ + src/locale/keymap-util.c | 208 ++++++++++++++++++++++++----------------------- + src/timedate/timedated.c | 21 ++++- + 4 files changed, 173 insertions(+), 111 deletions(-) + +diff --git a/src/basic/time-util.c b/src/basic/time-util.c +index 5318d63..fa3409d 100644 +--- a/src/basic/time-util.c ++++ b/src/basic/time-util.c +@@ -1456,19 +1456,43 @@ int get_timezone(char **ret) { + const char *e; + char *z; + int r; ++ bool use_utc_fallback = false; + + r = readlink_malloc("/etc/localtime", &t); +- if (r == -ENOENT) { +- /* If the symlink does not exist, assume "UTC", like glibc does*/ +- z = strdup("UTC"); ++ if (r < 0) { ++ if (r == -ENOENT) ++ use_utc_fallback = true; ++ else if (r != -EINVAL) ++ return r; /* returns EINVAL if not a symlink */ ++ ++ r = read_one_line_file("/etc/timezone", &t); ++ if (r < 0) { ++ if (r != -ENOENT) ++ log_warning_errno(r, "Failed to read /etc/timezone: %m"); ++ ++ if (use_utc_fallback) { ++ /* If the /etc/localtime symlink does not exist and we failed ++ * to read /etc/timezone, assume "UTC", like glibc does. */ ++ z = strdup("UTC"); ++ if (!z) ++ return -ENOMEM; ++ ++ *ret = z; ++ return 0; ++ } ++ ++ return -EINVAL; ++ } ++ ++ if (!timezone_is_valid(t, LOG_DEBUG)) ++ return -EINVAL; ++ z = strdup(t); + if (!z) + return -ENOMEM; + + *ret = z; + return 0; + } +- if (r < 0) +- return r; /* returns EINVAL if not a symlink */ + + e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/"); + if (!e) +diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c +index 64761dd..b4ea9e2 100644 +--- a/src/core/locale-setup.c ++++ b/src/core/locale-setup.c +@@ -58,6 +58,27 @@ int locale_setup(char ***environment) { + log_warning_errno(r, "Failed to read /etc/locale.conf: %m"); + } + ++ if (r <= 0) { ++ r = parse_env_file(NULL, "/etc/default/locale", ++ "LANG", &variables[VARIABLE_LANG], ++ "LANGUAGE", &variables[VARIABLE_LANGUAGE], ++ "LC_CTYPE", &variables[VARIABLE_LC_CTYPE], ++ "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC], ++ "LC_TIME", &variables[VARIABLE_LC_TIME], ++ "LC_COLLATE", &variables[VARIABLE_LC_COLLATE], ++ "LC_MONETARY", &variables[VARIABLE_LC_MONETARY], ++ "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES], ++ "LC_PAPER", &variables[VARIABLE_LC_PAPER], ++ "LC_NAME", &variables[VARIABLE_LC_NAME], ++ "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS], ++ "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE], ++ "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT], ++ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION]); ++ ++ if (r < 0 && r != -ENOENT) ++ log_warning_errno(r, "Failed to read /etc/default/locale: %m"); ++ } ++ + for (i = 0; i < _VARIABLE_LC_MAX; i++) { + char *s; + +diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c +index 697133a..4e1fedb 100644 +--- a/src/locale/keymap-util.c ++++ b/src/locale/keymap-util.c +@@ -95,6 +95,7 @@ void locale_simplify(char *locale[_VARIABLE_LC_MAX]) { + int locale_read_data(Context *c, sd_bus_message *m) { + struct stat st; + int r; ++ const char *path = "/etc/locale.conf"; + + /* Do not try to re-read the file within single bus operation. */ + if (m) { +@@ -105,7 +106,11 @@ int locale_read_data(Context *c, sd_bus_message *m) { + c->locale_cache = sd_bus_message_ref(m); + } + +- r = stat("/etc/locale.conf", &st); ++ r = stat(path, &st); ++ if (r < 0 && errno == ENOENT) { ++ path = "/etc/default/locale"; ++ r = stat(path, &st); ++ } + if (r < 0 && errno != ENOENT) + return -errno; + +@@ -120,7 +125,7 @@ int locale_read_data(Context *c, sd_bus_message *m) { + c->locale_mtime = t; + context_free_locale(c); + +- r = parse_env_file(NULL, "/etc/locale.conf", ++ r = parse_env_file(NULL, path, + "LANG", &c->locale[VARIABLE_LANG], + "LANGUAGE", &c->locale[VARIABLE_LANGUAGE], + "LC_CTYPE", &c->locale[VARIABLE_LC_CTYPE], +@@ -201,8 +206,6 @@ int vconsole_read_data(Context *c, sd_bus_message *m) { + } + + int x11_read_data(Context *c, sd_bus_message *m) { +- _cleanup_fclose_ FILE *f = NULL; +- bool in_section = false; + struct stat st; + usec_t t; + int r; +@@ -216,7 +219,7 @@ int x11_read_data(Context *c, sd_bus_message *m) { + c->x11_cache = sd_bus_message_ref(m); + } + +- if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) { ++ if (stat("/etc/default/keyboard", &st) < 0) { + if (errno != ENOENT) + return -errno; + +@@ -233,60 +236,14 @@ int x11_read_data(Context *c, sd_bus_message *m) { + c->x11_mtime = t; + context_free_x11(c); + +- f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); +- if (!f) +- return -errno; +- +- for (;;) { +- _cleanup_free_ char *line = NULL; +- char *l; +- +- r = read_line(f, LONG_LINE_MAX, &line); +- if (r < 0) +- return r; +- if (r == 0) +- break; +- +- l = strstrip(line); +- if (IN_SET(l[0], 0, '#')) +- continue; +- +- if (in_section && first_word(l, "Option")) { +- _cleanup_strv_free_ char **a = NULL; +- +- r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE); +- if (r < 0) +- return r; +- +- if (strv_length(a) == 3) { +- char **p = NULL; +- +- if (streq(a[1], "XkbLayout")) +- p = &c->x11_layout; +- else if (streq(a[1], "XkbModel")) +- p = &c->x11_model; +- else if (streq(a[1], "XkbVariant")) +- p = &c->x11_variant; +- else if (streq(a[1], "XkbOptions")) +- p = &c->x11_options; +- +- if (p) +- free_and_replace(*p, a[2]); +- } +- +- } else if (!in_section && first_word(l, "Section")) { +- _cleanup_strv_free_ char **a = NULL; ++ r = parse_env_file(NULL, "/etc/default/keyboard", ++ "XKBMODEL", &c->x11_model, ++ "XKBLAYOUT", &c->x11_layout, ++ "XKBVARIANT", &c->x11_variant, ++ "XKBOPTIONS", &c->x11_options); + +- r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE); +- if (r < 0) +- return -ENOMEM; +- +- if (strv_length(a) == 2 && streq(a[1], "InputClass")) +- in_section = true; +- +- } else if (in_section && first_word(l, "EndSection")) +- in_section = false; +- } ++ if (r < 0) ++ return r; + + return 0; + } +@@ -295,9 +252,18 @@ int locale_write_data(Context *c, char ***settings) { + _cleanup_strv_free_ char **l = NULL; + struct stat st; + int r, p; ++ const char *path = "/etc/locale.conf"; + + /* Set values will be returned as strv in *settings on success. */ + ++ r = load_env_file(NULL, path, &l); ++ if (r < 0 && r == -ENOENT) { ++ path = "/etc/default/locale"; ++ r = load_env_file(NULL, path, &l); ++ } ++ if (r < 0 && r != -ENOENT) ++ return r; ++ + for (p = 0; p < _VARIABLE_LC_MAX; p++) { + _cleanup_free_ char *t = NULL; + char **u; +@@ -320,20 +286,20 @@ int locale_write_data(Context *c, char ***settings) { + } + + if (strv_isempty(l)) { +- if (unlink("/etc/locale.conf") < 0) ++ if (unlink(path) < 0) + return errno == ENOENT ? 0 : -errno; + + c->locale_mtime = USEC_INFINITY; + return 0; + } + +- r = write_env_file_label("/etc/locale.conf", l); ++ r = write_env_file_label(path, l); + if (r < 0) + return r; + + *settings = TAKE_PTR(l); + +- if (stat("/etc/locale.conf", &st) >= 0) ++ if (stat(path, &st) >= 0) + c->locale_mtime = timespec_load(&st.st_mtim); + + return 0; +@@ -401,68 +367,104 @@ int vconsole_write_data(Context *c) { + } + + int x11_write_data(Context *c) { +- _cleanup_fclose_ FILE *f = NULL; +- _cleanup_free_ char *temp_path = NULL; + struct stat st; + int r; ++ char *t, **u, **l = NULL; + +- if (isempty(c->x11_layout) && +- isempty(c->x11_model) && +- isempty(c->x11_variant) && +- isempty(c->x11_options)) { ++ r = load_env_file(NULL, "/etc/default/keyboard", &l); ++ if (r < 0 && r != -ENOENT) ++ return r; + +- if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) +- return errno == ENOENT ? 0 : -errno; ++ /* This could perhaps be done more elegantly using an array ++ * like we do for the locale, instead of struct ++ */ ++ if (isempty(c->x11_layout)) { ++ l = strv_env_unset(l, "XKBLAYOUT"); ++ } else { ++ if (asprintf(&t, "XKBLAYOUT=%s", c->x11_layout) < 0) { ++ strv_free(l); ++ return -ENOMEM; ++ } + +- c->vc_mtime = USEC_INFINITY; +- return 0; ++ u = strv_env_set(l, t); ++ free(t); ++ strv_free(l); ++ ++ if (!u) ++ return -ENOMEM; ++ ++ l = u; + } + +- (void) mkdir_p_label("/etc/X11/xorg.conf.d", 0755); +- r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); +- if (r < 0) +- return r; ++ if (isempty(c->x11_model)) { ++ l = strv_env_unset(l, "XKBMODEL"); ++ } else { ++ if (asprintf(&t, "XKBMODEL=%s", c->x11_model) < 0) { ++ strv_free(l); ++ return -ENOMEM; ++ } + +- (void) fchmod(fileno(f), 0644); ++ u = strv_env_set(l, t); ++ free(t); ++ strv_free(l); + +- fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n" +- "# probably wise not to edit this file manually. Use localectl(1) to\n" +- "# instruct systemd-localed to update it.\n" +- "Section \"InputClass\"\n" +- " Identifier \"system-keyboard\"\n" +- " MatchIsKeyboard \"on\"\n", f); ++ if (!u) ++ return -ENOMEM; ++ ++ l = u; ++ } ++ ++ if (isempty(c->x11_variant)) { ++ l = strv_env_unset(l, "XKBVARIANT"); ++ } else { ++ if (asprintf(&t, "XKBVARIANT=%s", c->x11_variant) < 0) { ++ strv_free(l); ++ return -ENOMEM; ++ } + +- if (!isempty(c->x11_layout)) +- fprintf(f, " Option \"XkbLayout\" \"%s\"\n", c->x11_layout); ++ u = strv_env_set(l, t); ++ free(t); ++ strv_free(l); + +- if (!isempty(c->x11_model)) +- fprintf(f, " Option \"XkbModel\" \"%s\"\n", c->x11_model); ++ if (!u) ++ return -ENOMEM; + +- if (!isempty(c->x11_variant)) +- fprintf(f, " Option \"XkbVariant\" \"%s\"\n", c->x11_variant); ++ l = u; ++ } + +- if (!isempty(c->x11_options)) +- fprintf(f, " Option \"XkbOptions\" \"%s\"\n", c->x11_options); ++ if (isempty(c->x11_options)) { ++ l = strv_env_unset(l, "XKBOPTIONS"); ++ } else { ++ if (asprintf(&t, "XKBOPTIONS=%s", c->x11_options) < 0) { ++ strv_free(l); ++ return -ENOMEM; ++ } + +- fputs("EndSection\n", f); ++ u = strv_env_set(l, t); ++ free(t); ++ strv_free(l); + +- r = fflush_sync_and_check(f); +- if (r < 0) +- goto fail; ++ if (!u) ++ return -ENOMEM; + +- if (rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) { +- r = -errno; +- goto fail; ++ l = u; + } + +- if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) >= 0) +- c->x11_mtime = timespec_load(&st.st_mtim); ++ if (strv_isempty(l)) { ++ strv_free(l); + +- return 0; ++ if (unlink("/etc/default/keyboard") < 0) ++ return errno == ENOENT ? 0 : -errno; + +-fail: +- if (temp_path) +- (void) unlink(temp_path); ++ c->vc_mtime = USEC_INFINITY; ++ return 0; ++ } ++ ++ r = write_env_file("/etc/default/keyboard", l); ++ strv_free(l); ++ ++ if (r >= 0 && stat("/etc/default/keyboard", &st) >= 0) ++ c->x11_mtime = timespec_load(&st.st_mtim); + + return r; + } +diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c +index 8149fac..42c8277 100644 +--- a/src/timedate/timedated.c ++++ b/src/timedate/timedated.c +@@ -283,6 +283,8 @@ static int context_read_data(Context *c) { + static int context_write_data_timezone(Context *c) { + _cleanup_free_ char *p = NULL; + const char *source; ++ int r = 0; ++ struct stat st; + + assert(c); + +@@ -296,9 +298,12 @@ static int context_write_data_timezone(Context *c) { + if (access("/usr/share/zoneinfo/UTC", F_OK) < 0) { + + if (unlink("/etc/localtime") < 0 && errno != ENOENT) +- return -errno; ++ r = -errno; + +- return 0; ++ if (unlink("/etc/timezone") < 0 && errno != ENOENT) ++ r = -errno; ++ ++ return r; + } + + source = "../usr/share/zoneinfo/UTC"; +@@ -310,7 +315,17 @@ static int context_write_data_timezone(Context *c) { + source = p; + } + +- return symlink_atomic(source, "/etc/localtime"); ++ r = symlink_atomic(source, "/etc/localtime"); ++ if (r < 0) ++ return r; ++ ++ if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) { ++ r = write_string_file("/etc/timezone", c->zone, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); ++ if (r < 0) ++ return r; ++ } ++ ++ return 0; + } + + static int context_write_data_local_rtc(Context *c) { diff --git a/debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch b/debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch new file mode 100644 index 0000000..ee49d27 --- /dev/null +++ b/debian/patches/debian/deny-list-upstream-test-02-ppc64el.patch @@ -0,0 +1,17 @@ +From: Dan Streetman <ddstreet@canonical.com> +Date: Wed, 6 Nov 2019 09:14:54 -0500 +Subject: deny-list-upstream-test-02-ppc64el + +Bug: https://github.com/systemd/systemd/issues/11612 +--- + test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el + +diff --git a/test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el b/test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el +new file mode 100644 +index 0000000..52877fc +--- /dev/null ++++ b/test/TEST-02-UNITTESTS/deny-list-upstream-ci-ppc64el +@@ -0,0 +1 @@ ++# unknown reason for failing, tracked in https://github.com/systemd/systemd/issues/11612 diff --git a/debian/patches/debian/deny-list-upstream-test-25.patch b/debian/patches/debian/deny-list-upstream-test-25.patch new file mode 100644 index 0000000..ec6c211 --- /dev/null +++ b/debian/patches/debian/deny-list-upstream-test-25.patch @@ -0,0 +1,17 @@ +From: Dan Streetman <ddstreet@canonical.com> +Date: Wed, 6 Nov 2019 09:14:50 -0500 +Subject: deny-list-upstream-test-25 + +Bug: https://github.com/systemd/systemd/issues/13973 +--- + test/TEST-25-IMPORT/deny-list-upstream-ci | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 test/TEST-25-IMPORT/deny-list-upstream-ci + +diff --git a/test/TEST-25-IMPORT/deny-list-upstream-ci b/test/TEST-25-IMPORT/deny-list-upstream-ci +new file mode 100644 +index 0000000..47a5f15 +--- /dev/null ++++ b/test/TEST-25-IMPORT/deny-list-upstream-ci +@@ -0,0 +1 @@ ++# unknown failure; tracked in https://github.com/systemd/systemd/issues/13973 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..6e61355 --- /dev/null +++ b/debian/patches/debian/fsckd-daemon-for-inter-fsckd-communication.patch @@ -0,0 +1,1065 @@ +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 | 9 + + po/POTFILES.in | 1 + + src/fsckd/fsckd.c | 699 +++++++++++++++++++++++++++++++++++++ + 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 + + 10 files changed, 910 insertions(+), 1 deletion(-) + create mode 100644 man/systemd-fsckd.service.xml + create mode 100644 src/fsckd/fsckd.c + 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 cacbbd7..285f5b8 100644 +--- a/man/rules/meson.build ++++ b/man/rules/meson.build +@@ -840,6 +840,7 @@ manpages = [ + '8', + ['systemd-fsck', 'systemd-fsck-root.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:<num_devices>:<progress>:<string></literal> ++ <variablelist> ++ <varlistentry> ++ <term><literal><num_devices></literal></term> ++ <listitem><para>the current number of devices ++ being checked (int)</para></listitem> ++ </varlistentry> ++ <varlistentry> ++ <term><literal><progress></literal></term> ++ <listitem><para>the current minimum percentage of ++ all devices being checking (float, from 0 to 100)</para></listitem> ++ </varlistentry> ++ <varlistentry> ++ <term><literal><string></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:<string></literal> ++ <variablelist> ++ <varlistentry> ++ <term><literal><strings></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 cf93f38..e33b729 100644 +--- a/meson.build ++++ b/meson.build +@@ -2895,6 +2895,15 @@ executable( + install : true, + install_dir : rootlibexecdir) + ++executable( ++ 'systemd-fsckd', ++ 'src/fsckd/fsckd.c', ++ include_directories : includes, ++ link_with : [libshared], ++ install_rpath : rootlibexecdir, ++ install : true, ++ install_dir : rootlibexecdir) ++ + executable( + 'systemd-sleep', + 'src/sleep/sleep.c', +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 0346a19..5ea2444 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -10,3 +10,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..4af8e45 +--- /dev/null ++++ b/src/fsckd/fsckd.c +@@ -0,0 +1,699 @@ ++/*-*- 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 "def.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 "util.h" ++#include "alloc-util.h" ++#include "locale-util.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 void client_free(Client *c); ++static void manager_free(Manager *m); ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(Client*, client_free); ++DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); ++ ++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 void 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); ++ free(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; ++ Client *current; ++ 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 = PLYMOUTH_SOCKET; ++ 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(plymouth_fd, packet, n + 1, true); ++} ++ ++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(m->plymouth_fd, PLYMOUTH_REQUEST_KEY, sizeof(PLYMOUTH_REQUEST_KEY), true); ++ 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) { ++ Client *current = NULL; ++ _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 void manager_free(Manager *m) { ++ if (!m) ++ return; ++ ++ /* 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); ++ ++ free(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, "hv", options, NULL)) >= 0) ++ switch (c) { ++ ++ case 'h': ++ help(); ++ return 0; ++ ++ case ARG_VERSION: ++ version(); ++ return 0; ++ ++ case '?': ++ return -EINVAL; ++ ++ default: ++ assert_not_reached("Unhandled option"); ++ } ++ ++ 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/units/meson.build b/units/meson.build +index ba60eb7..95d7eda 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -103,6 +103,7 @@ units = [ + ['systemd-exit.service', ''], + ['systemd-firstboot.service', 'ENABLE_FIRSTBOOT', + 'sysinit.target.wants/'], ++ ['systemd-fsckd.socket', ''], + ['systemd-halt.service', ''], + ['systemd-homed-activate.service', 'ENABLE_HOMED'], + ['systemd-initctl.socket', 'HAVE_SYSV_COMPAT', +@@ -174,6 +175,7 @@ in_units = [ + ['systemd-pstore.service', 'ENABLE_PSTORE'], + ['systemd-fsck-root.service', ''], + ['systemd-fsck@.service', ''], ++ ['systemd-fsckd.service', ''], + ['systemd-hibernate-resume@.service', 'ENABLE_HIBERNATE'], + ['systemd-hibernate.service', 'ENABLE_HIBERNATE'], + ['systemd-hybrid-sleep.service', 'ENABLE_HIBERNATE'], +diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in +index c4a2948..1dce176 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=!/ + + [Service] +diff --git a/units/systemd-fsck@.service.in b/units/systemd-fsck@.service.in +index 6d9c9ab..48d5f29 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..9c7ed51 +--- /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=@rootlibexecdir@/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/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..68fca31 --- /dev/null +++ b/debian/patches/debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch @@ -0,0 +1,52 @@ +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 | 8 ++++---- + src/systemctl/systemctl-logind.c | 3 ++- + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c +index 8e41bd6..239a780 100644 +--- a/src/systemctl/systemctl-compat-halt.c ++++ b/src/systemctl/systemctl-compat-halt.c +@@ -149,11 +149,11 @@ int halt_main(void) { + if (r < 0) + return r; + +- /* Delayed shutdown requested, and was successful */ +- if (arg_when > 0 && logind_schedule_shutdown() == 0) +- return 0; ++ /* Delayed shutdown requested */ ++ if (arg_when > 0) ++ return logind_schedule_shutdown(); + +- /* No delay, or logind failed or is not at all available */ ++ /* No delay, or logind is not at all available */ + if (geteuid() != 0) { + if (arg_dry_run || arg_force > 0) { + (void) must_be_root(); +diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c +index 405f12a..fd0b143 100644 +--- a/src/systemctl/systemctl-logind.c ++++ b/src/systemctl/systemctl-logind.c +@@ -315,8 +315,9 @@ int logind_schedule_shutdown(void) { + (void) logind_set_wall_message(); + + r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when); ++ /* logind fails, cannot schedule reboot, do nothing */ + if (r < 0) +- return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r)); ++ return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, no action will be taken: %s", bus_error_message(&error, r)); + + if (!arg_quiet) + log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", log_action, format_timestamp_style(date, sizeof(date), arg_when, arg_timestamp_style)); diff --git a/debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch b/debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch new file mode 100644 index 0000000..0a48308 --- /dev/null +++ b/debian/patches/debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch @@ -0,0 +1,131 @@ +From: Michael Biebl <biebl@debian.org> +Date: Mon, 18 Jan 2021 13:33:10 +0100 +Subject: test: disable DnsmasqClientTest.test_resolved_etc_hosts in + networkd-test.py + +This test appears to be flaky. + +See: #979716 +--- + test/networkd-test.py | 108 +++++++++++++++++++++++++------------------------- + 1 file changed, 54 insertions(+), 54 deletions(-) + +diff --git a/test/networkd-test.py b/test/networkd-test.py +index 8496ec8..ca158bf 100755 +--- a/test/networkd-test.py ++++ b/test/networkd-test.py +@@ -674,60 +674,60 @@ Domains= ~company ~lab''') + self.assertRegex(general_log, 'query.*megasearch.net') + self.assertNotIn('megasearch.net', vpn_log) + +- def test_resolved_etc_hosts(self): +- '''resolved queries to /etc/hosts''' +- +- # FIXME: -t MX query fails with enabled DNSSEC (even when using +- # the known negative trust anchor .internal instead of .example.com) +- conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf' +- os.makedirs(os.path.dirname(conf), exist_ok=True) +- with open(conf, 'w') as f: +- f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n') +- self.addCleanup(os.remove, conf) +- +- # create /etc/hosts bind mount which resolves my.example.com for IPv4 +- hosts = os.path.join(self.workdir, 'hosts') +- with open(hosts, 'w') as f: +- f.write('172.16.99.99 my.example.com\n') +- subprocess.check_call(['mount', '--bind', hosts, '/etc/hosts']) +- self.addCleanup(subprocess.call, ['umount', '/etc/hosts']) +- subprocess.check_call(['systemctl', 'stop', 'systemd-resolved.service']) +- +- # note: different IPv4 address here, so that it's easy to tell apart +- # what resolved the query +- self.create_iface(dnsmasq_opts=['--host-record=my.example.com,172.16.99.1,2600::99:99', +- '--host-record=other.example.com,172.16.0.42,2600::42', +- '--mx-host=example.com,mail.example.com'], +- ipv6=True) +- self.do_test(coldplug=None, ipv6=True) +- +- try: +- # family specific queries +- out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example.com']) +- self.assertIn(b'my.example.com: 172.16.99.99', out) +- # we don't expect an IPv6 answer; if /etc/hosts has any IP address, +- # it's considered a sufficient source +- self.assertNotEqual(subprocess.call(['resolvectl', 'query', '-6', 'my.example.com']), 0) +- # "any family" query; IPv4 should come from /etc/hosts +- out = subprocess.check_output(['resolvectl', 'query', 'my.example.com']) +- self.assertIn(b'my.example.com: 172.16.99.99', out) +- # IP → name lookup; again, takes the /etc/hosts one +- out = subprocess.check_output(['resolvectl', 'query', '172.16.99.99']) +- self.assertIn(b'172.16.99.99: my.example.com', out) +- +- # non-address RRs should fall back to DNS +- out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example.com']) +- self.assertIn(b'example.com IN MX 1 mail.example.com', out) +- +- # other domains query DNS +- out = subprocess.check_output(['resolvectl', 'query', 'other.example.com']) +- self.assertIn(b'172.16.0.42', out) +- out = subprocess.check_output(['resolvectl', 'query', '172.16.0.42']) +- self.assertIn(b'172.16.0.42: other.example.com', out) +- except (AssertionError, subprocess.CalledProcessError): +- self.show_journal('systemd-resolved.service') +- self.print_server_log() +- raise ++# def test_resolved_etc_hosts(self): ++# '''resolved queries to /etc/hosts''' ++# ++# # FIXME: -t MX query fails with enabled DNSSEC (even when using ++# # the known negative trust anchor .internal instead of .example.com) ++# conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf' ++# os.makedirs(os.path.dirname(conf), exist_ok=True) ++# with open(conf, 'w') as f: ++# f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n') ++# self.addCleanup(os.remove, conf) ++# ++# # create /etc/hosts bind mount which resolves my.example.com for IPv4 ++# hosts = os.path.join(self.workdir, 'hosts') ++# with open(hosts, 'w') as f: ++# f.write('172.16.99.99 my.example.com\n') ++# subprocess.check_call(['mount', '--bind', hosts, '/etc/hosts']) ++# self.addCleanup(subprocess.call, ['umount', '/etc/hosts']) ++# subprocess.check_call(['systemctl', 'stop', 'systemd-resolved.service']) ++# ++# # note: different IPv4 address here, so that it's easy to tell apart ++# # what resolved the query ++# self.create_iface(dnsmasq_opts=['--host-record=my.example.com,172.16.99.1,2600::99:99', ++# '--host-record=other.example.com,172.16.0.42,2600::42', ++# '--mx-host=example.com,mail.example.com'], ++# ipv6=True) ++# self.do_test(coldplug=None, ipv6=True) ++# ++# try: ++# # family specific queries ++# out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example.com']) ++# self.assertIn(b'my.example.com: 172.16.99.99', out) ++# # we don't expect an IPv6 answer; if /etc/hosts has any IP address, ++# # it's considered a sufficient source ++# self.assertNotEqual(subprocess.call(['resolvectl', 'query', '-6', 'my.example.com']), 0) ++# # "any family" query; IPv4 should come from /etc/hosts ++# out = subprocess.check_output(['resolvectl', 'query', 'my.example.com']) ++# self.assertIn(b'my.example.com: 172.16.99.99', out) ++# # IP → name lookup; again, takes the /etc/hosts one ++# out = subprocess.check_output(['resolvectl', 'query', '172.16.99.99']) ++# self.assertIn(b'172.16.99.99: my.example.com', out) ++# ++# # non-address RRs should fall back to DNS ++# out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example.com']) ++# self.assertIn(b'example.com IN MX 1 mail.example.com', out) ++# ++# # other domains query DNS ++# out = subprocess.check_output(['resolvectl', 'query', 'other.example.com']) ++# self.assertIn(b'172.16.0.42', out) ++# out = subprocess.check_output(['resolvectl', 'query', '172.16.0.42']) ++# self.assertIn(b'172.16.0.42: other.example.com', out) ++# except (AssertionError, subprocess.CalledProcessError): ++# self.show_journal('systemd-resolved.service') ++# self.print_server_log() ++# raise + + def test_transient_hostname(self): + '''networkd sets transient hostname from DHCP''' diff --git a/debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch b/debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch new file mode 100644 index 0000000..f5432e8 --- /dev/null +++ b/debian/patches/debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch @@ -0,0 +1,25 @@ +From: Michael Biebl <biebl@debian.org> +Date: Tue, 19 Nov 2019 09:10:23 +0100 +Subject: udev: drop SystemCallArchitectures=native from systemd-udevd.service + +We can't really control what helper programs are run from other udev +rules. E.g. running i386 binaries under amd64 is a valid use case and +should not trigger a SIGSYS failure. + +Closes: #869719 +--- + units/systemd-udevd.service.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in +index 225eac2..f541ff6 100644 +--- a/units/systemd-udevd.service.in ++++ b/units/systemd-udevd.service.in +@@ -35,7 +35,6 @@ MemoryDenyWriteExecute=yes + RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 + RestrictRealtime=yes + RestrictSUIDSGID=yes +-SystemCallArchitectures=native + LockPersonality=yes + IPAddressDeny=any + @SERVICE_WATCHDOG@ diff --git a/debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch b/debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch new file mode 100644 index 0000000..6752b9d --- /dev/null +++ b/debian/patches/localed-Run-locale-gen-if-available-to-generate-missing-l.patch @@ -0,0 +1,448 @@ +From: Matthias Klumpp <matthias@tenstral.net> +Date: Fri, 8 Jan 2021 23:59:38 +0100 +Subject: localed: Run locale-gen if available to generate missing locale + +This change improves integration with distributions using locale-gen to +generate missing locale on-demand, like Debian-based distributions +(Debian/Ubuntu/PureOS/Tanglu/...) and Arch Linux. +We only ever enable new locales for generation, and never disable them. +Furthermore, we only generate UTF-8 locale. + +This feature is only used if explicitly enabled at compile-time, and +will also be inert at runtime if the locale-gen binary is missing. + +(cherry picked from commit 8f20232fcb52dbe6255f3df6101fc057af90bcfa) +--- + meson.build | 8 ++ + meson_options.txt | 2 + + src/locale/keymap-util.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++ + src/locale/keymap-util.h | 4 + + src/locale/localectl.c | 6 +- + src/locale/localed.c | 59 ++++++++++++- + 6 files changed, 286 insertions(+), 4 deletions(-) + +diff --git a/meson.build b/meson.build +index 580964c..cf93f38 100644 +--- a/meson.build ++++ b/meson.build +@@ -833,6 +833,14 @@ if default_locale == '' + endif + conf.set_quoted('SYSTEMD_DEFAULT_LOCALE', default_locale) + ++localegen_path = get_option('localegen-path') ++have = false ++if localegen_path != '' ++ conf.set_quoted('LOCALEGEN_PATH', localegen_path) ++ have = true ++endif ++conf.set10('HAVE_LOCALEGEN', have) ++ + conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) + + service_watchdog = get_option('service-watchdog') +diff --git a/meson_options.txt b/meson_options.txt +index 2435cce..4ffdf7f 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -238,6 +238,8 @@ option('gshadow', type : 'boolean', + description : 'support for shadow group') + option('default-locale', type : 'string', value : '', + description : 'default locale used when /etc/locale.conf does not exist') ++option('localegen-path', type : 'string', value : '', ++ description : 'absolute path to the locale-gen binary in case the system is using locale-gen') + option('service-watchdog', type : 'string', value : '3min', + description : 'default watchdog setting for systemd services') + +diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c +index cb8153f..697133a 100644 +--- a/src/locale/keymap-util.c ++++ b/src/locale/keymap-util.c +@@ -6,18 +6,21 @@ + #include <unistd.h> + + #include "bus-polkit.h" ++#include "copy.h" + #include "env-file-label.h" + #include "env-file.h" + #include "env-util.h" + #include "fd-util.h" + #include "fileio-label.h" + #include "fileio.h" ++#include "fs-util.h" + #include "kbd-util.h" + #include "keymap-util.h" + #include "locale-util.h" + #include "macro.h" + #include "mkdir.h" + #include "nulstr-util.h" ++#include "process-util.h" + #include "string-util.h" + #include "strv.h" + #include "tmpfile-util.h" +@@ -780,3 +783,211 @@ int x11_convert_to_vconsole(Context *c) { + + return modified; + } ++ ++bool locale_gen_check_available(void) { ++#if HAVE_LOCALEGEN ++ if (access(LOCALEGEN_PATH, X_OK) < 0) { ++ if (errno != ENOENT) ++ log_warning_errno(errno, "Unable to determine whether " LOCALEGEN_PATH " exists and is executable, assuming it is not: %m"); ++ return false; ++ } ++ if (access("/etc/locale.gen", F_OK) < 0) { ++ if (errno != ENOENT) ++ log_warning_errno(errno, "Unable to determine whether /etc/locale.gen exists, assuming it does not: %m"); ++ return false; ++ } ++ return true; ++#else ++ return false; ++#endif ++} ++ ++#if HAVE_LOCALEGEN ++static bool locale_encoding_is_utf8_or_unspecified(const char *locale) { ++ const char *c = strchr(locale, '.'); ++ return !c || strcaseeq(c, ".UTF-8") || strcasestr(locale, ".UTF-8@"); ++} ++ ++static int locale_gen_locale_supported(const char *locale_entry) { ++ /* Returns an error valus <= 0 if the locale-gen entry is invalid or unsupported, ++ * 1 in case the locale entry is valid, and -EOPNOTSUPP specifically in case ++ * the distributor has not provided us with a SUPPORTED file to check ++ * locale for validity. */ ++ ++ _cleanup_fclose_ FILE *f = NULL; ++ int r; ++ ++ assert(locale_entry); ++ ++ /* Locale templates without country code are never supported */ ++ if (!strstr(locale_entry, "_")) ++ return -EINVAL; ++ ++ f = fopen("/usr/share/i18n/SUPPORTED", "re"); ++ if (!f) { ++ if (errno == ENOENT) ++ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), ++ "Unable to check validity of locale entry %s: /usr/share/i18n/SUPPORTED does not exist", ++ locale_entry); ++ return -errno; ++ } ++ ++ for (;;) { ++ _cleanup_free_ char *line = NULL; ++ ++ r = read_line(f, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to read /usr/share/i18n/SUPPORTED: %m"); ++ if (r == 0) ++ return 0; ++ ++ line = strstrip(line); ++ if (strcaseeq_ptr(line, locale_entry)) ++ return 1; ++ } ++} ++#endif ++ ++int locale_gen_enable_locale(const char *locale) { ++#if HAVE_LOCALEGEN ++ _cleanup_fclose_ FILE *fr = NULL, *fw = NULL; ++ _cleanup_(unlink_and_freep) char *temp_path = NULL; ++ _cleanup_free_ char *locale_entry = NULL; ++ bool locale_enabled = false, first_line = false; ++ bool write_new = false; ++ int r; ++ ++ if (isempty(locale)) ++ return 0; ++ ++ if (locale_encoding_is_utf8_or_unspecified(locale)) { ++ locale_entry = strjoin(locale, " UTF-8"); ++ if (!locale_entry) ++ return -ENOMEM; ++ } else ++ return -ENOEXEC; /* We do not process non-UTF-8 locale */ ++ ++ r = locale_gen_locale_supported(locale_entry); ++ if (r == 0) ++ return -EINVAL; ++ if (r < 0 && r != -EOPNOTSUPP) ++ return r; ++ ++ fr = fopen("/etc/locale.gen", "re"); ++ if (!fr) { ++ if (errno != ENOENT) ++ return -errno; ++ write_new = true; ++ } ++ ++ r = fopen_temporary("/etc/locale.gen", &fw, &temp_path); ++ if (r < 0) ++ return r; ++ ++ if (write_new) ++ (void) fchmod(fileno(fw), 0644); ++ else { ++ /* apply mode & xattrs of the original file to new file */ ++ r = copy_access(fileno(fr), fileno(fw)); ++ if (r < 0) ++ return r; ++ r = copy_xattr(fileno(fr), fileno(fw)); ++ if (r < 0) ++ return r; ++ } ++ ++ if (!write_new) { ++ /* The config file ends with a line break, which we do not want to include before potentially appending a new locale ++ * instead of uncommenting an existing line. By prepending linebreaks, we can avoid buffering this file but can still write ++ * a nice config file without empty lines */ ++ first_line = true; ++ for (;;) { ++ _cleanup_free_ char *line = NULL; ++ char *line_locale; ++ ++ r = read_line(fr, LONG_LINE_MAX, &line); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ if (locale_enabled) { ++ /* Just complete writing the file if the new locale was already enabled */ ++ if (!first_line) ++ fputc('\n', fw); ++ fputs(line, fw); ++ first_line = false; ++ continue; ++ } ++ ++ line = strstrip(line); ++ if (isempty(line)) { ++ fputc('\n', fw); ++ first_line = false; ++ continue; ++ } ++ ++ line_locale = line; ++ if (line_locale[0] == '#') ++ line_locale = strstrip(line_locale + 1); ++ else if (strcaseeq_ptr(line_locale, locale_entry)) ++ return 0; /* the file already had our locale activated, so skip updating it */ ++ ++ if (strcaseeq_ptr(line_locale, locale_entry)) { ++ /* Uncomment existing line for new locale */ ++ if (!first_line) ++ fputc('\n', fw); ++ fputs(locale_entry, fw); ++ locale_enabled = true; ++ first_line = false; ++ continue; ++ } ++ ++ /* The line was not for the locale we want to enable, just copy it */ ++ if (!first_line) ++ fputc('\n', fw); ++ fputs(line, fw); ++ first_line = false; ++ } ++ } ++ ++ /* Add locale to enable to the end of the file if it was not found as commented line */ ++ if (!locale_enabled) { ++ if (!write_new) ++ fputc('\n', fw); ++ fputs(locale_entry, fw); ++ } ++ fputc('\n', fw); ++ ++ r = fflush_sync_and_check(fw); ++ if (r < 0) ++ return r; ++ ++ if (rename(temp_path, "/etc/locale.gen") < 0) ++ return -errno; ++ temp_path = mfree(temp_path); ++ ++ return 0; ++#else ++ return -EOPNOTSUPP; ++#endif ++} ++ ++int locale_gen_run(void) { ++#if HAVE_LOCALEGEN ++ pid_t pid; ++ int r; ++ ++ r = safe_fork("(sd-localegen)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, &pid); ++ if (r < 0) ++ return r; ++ if (r == 0) { ++ execl(LOCALEGEN_PATH, LOCALEGEN_PATH, NULL); ++ _exit(EXIT_FAILURE); ++ } ++ ++ return 0; ++#else ++ return -EOPNOTSUPP; ++#endif ++} +diff --git a/src/locale/keymap-util.h b/src/locale/keymap-util.h +index 4997647..c087dbc 100644 +--- a/src/locale/keymap-util.h ++++ b/src/locale/keymap-util.h +@@ -42,3 +42,7 @@ int x11_convert_to_vconsole(Context *c); + int x11_write_data(Context *c); + void locale_simplify(char *locale[_VARIABLE_LC_MAX]); + int locale_write_data(Context *c, char ***settings); ++ ++bool locale_gen_check_available(void); ++int locale_gen_enable_locale(const char *locale); ++int locale_gen_run(void); +diff --git a/src/locale/localectl.c b/src/locale/localectl.c +index 7d2e887..7d3d3f8 100644 +--- a/src/locale/localectl.c ++++ b/src/locale/localectl.c +@@ -26,6 +26,9 @@ + #include "verbs.h" + #include "virt.h" + ++/* Enough time for locale-gen to finish server-side (in case it is in use) */ ++#define LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE) ++ + static PagerFlags arg_pager_flags = 0; + static bool arg_ask_password = true; + static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +@@ -176,7 +179,8 @@ static int set_locale(int argc, char **argv, void *userdata) { + if (r < 0) + return bus_log_create_error(r); + +- r = sd_bus_call(bus, m, 0, &error, NULL); ++ /* We use a longer timeout for the method call in case localed is running locale-gen */ ++ r = sd_bus_call(bus, m, LOCALE_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL); + if (r < 0) + return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); + +diff --git a/src/locale/localed.c b/src/locale/localed.c +index 736dacd..12073bd 100644 +--- a/src/locale/localed.c ++++ b/src/locale/localed.c +@@ -262,6 +262,7 @@ static int property_get_xkb( + static int process_locale_list_item( + const char *assignment, + char *new_locale[static _VARIABLE_LC_MAX], ++ bool use_localegen, + sd_bus_error *error) { + + assert(assignment); +@@ -283,7 +284,7 @@ static int process_locale_list_item( + + if (!locale_is_valid(e)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e); +- if (locale_is_installed(e) <= 0) ++ if (!use_localegen && locale_is_installed(e) <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e); + if (new_locale[p]) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name); +@@ -298,6 +299,47 @@ static int process_locale_list_item( + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment); + } + ++static int locale_gen_process_locale(char *new_locale[static _VARIABLE_LC_MAX], ++ sd_bus_error *error) { ++ int r; ++ assert(new_locale); ++ ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { ++ if (p == VARIABLE_LANGUAGE) ++ continue; ++ if (isempty(new_locale[p])) ++ continue; ++ if (locale_is_installed(new_locale[p])) ++ continue; ++ ++ r = locale_gen_enable_locale(new_locale[p]); ++ if (r == -ENOEXEC) { ++ log_error_errno(r, "Refused to enable locale for generation: %m"); ++ return sd_bus_error_setf(error, ++ SD_BUS_ERROR_INVALID_ARGS, ++ "Specified locale is not installed and non-UTF-8 locale will not be auto-generated: %s", ++ new_locale[p]); ++ } else if (r == -EINVAL) { ++ log_error_errno(r, "Failed to enable invalid locale %s for generation.", new_locale[p]); ++ return sd_bus_error_setf(error, ++ SD_BUS_ERROR_INVALID_ARGS, ++ "Can not enable locale generation for invalid locale: %s", ++ new_locale[p]); ++ } else if (r < 0) { ++ log_error_errno(r, "Failed to enable locale for generation: %m"); ++ return sd_bus_error_set_errnof(error, r, "Failed to enable locale generation: %m"); ++ } ++ ++ r = locale_gen_run(); ++ if (r < 0) { ++ log_error_errno(r, "Failed to generate locale: %m"); ++ return sd_bus_error_set_errnof(error, r, "Failed to generate locale: %m"); ++ } ++ } ++ ++ return 0; ++} ++ + static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { + _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {}; + _cleanup_strv_free_ char **settings = NULL, **l = NULL; +@@ -305,6 +347,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + bool modified = false; + int interactive, r; + char **i; ++ bool use_localegen; + + assert(m); + assert(c); +@@ -317,11 +360,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + if (r < 0) + return r; + ++ use_localegen = locale_gen_check_available(); ++ + /* If single locale without variable name is provided, then we assume it is LANG=. */ + if (strv_length(l) == 1 && !strchr(l[0], '=')) { + if (!locale_is_valid(l[0])) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]); +- if (locale_is_installed(l[0]) <= 0) ++ if (!use_localegen && locale_is_installed(l[0]) <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]); + + new_locale[VARIABLE_LANG] = strdup(l[0]); +@@ -333,7 +378,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + + /* Check whether a variable is valid */ + STRV_FOREACH(i, l) { +- r = process_locale_list_item(*i, new_locale, error); ++ r = process_locale_list_item(*i, new_locale, use_localegen, error); + if (r < 0) + return r; + } +@@ -392,9 +437,17 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + ++ /* Generate locale in case it is missing and the system is using locale-gen */ ++ if (use_localegen) { ++ r = locale_gen_process_locale(new_locale, error); ++ if (r < 0) ++ return r; ++ } ++ + for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + free_and_replace(c->locale[p], new_locale[p]); + ++ /* Write locale configuration */ + r = locale_write_data(c, &settings); + if (r < 0) { + log_error_errno(r, "Failed to set locale: %m"); diff --git a/debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch b/debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch new file mode 100644 index 0000000..509dfc7 --- /dev/null +++ b/debian/patches/logind-fix-getting-property-OnExternalPower-via-D-Bus.patch @@ -0,0 +1,36 @@ +From: Michael Biebl <biebl@debian.org> +Date: Wed, 12 Oct 2022 11:07:57 +0200 +Subject: logind: fix getting property OnExternalPower via D-Bus + +The BUS_DEFINE_PROPERTY_GET_GLOBAL macro requires a value as third +argument, so we need to call manager_is_on_external_power(). Otherwise +the function pointer is interpreted as a boolean and always returns +true: + +``` +$ busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager OnExternalPower +b true +$ /lib/systemd/systemd-ac-power --verbose +no +``` + +Thanks: Helmut Grohne <helmut@subdivi.de> +Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1021644 +(cherry picked from commit 63168cb517a556b2f4f175b365f5a4b4c7e85150) +--- + src/login/logind-dbus.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c +index b95af1a..cf2be79 100644 +--- a/src/login/logind-dbus.c ++++ b/src/login/logind-dbus.c +@@ -352,7 +352,7 @@ static int property_get_scheduled_shutdown( + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_handle_action, handle_action, HandleAction); + static BUS_DEFINE_PROPERTY_GET(property_get_docked, "b", Manager, manager_is_docked_or_external_displays); + static BUS_DEFINE_PROPERTY_GET(property_get_lid_closed, "b", Manager, manager_is_lid_closed); +-static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_on_external_power, "b", manager_is_on_external_power); ++static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_on_external_power, "b", manager_is_on_external_power()); + static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_compat_user_tasks_max, "t", CGROUP_LIMIT_MAX); + static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "t", Hashmap *, (uint64_t) hashmap_size); + diff --git a/debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch b/debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch new file mode 100644 index 0000000..50ae974 --- /dev/null +++ b/debian/patches/machine-adjust-error-message-to-use-normalized-instead-of.patch @@ -0,0 +1,28 @@ +From: Luca Boccassi <luca.boccassi@microsoft.com> +Date: Tue, 15 Dec 2020 18:26:34 +0000 +Subject: machine: adjust error message to use 'normalized' instead of ../ + +(cherry picked from commit 724e689715c8d9f23d035ab20d8c87b6b6c06e33) +--- + src/machine/machine-dbus.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c +index 3c8f4fd..5ed892f 100644 +--- a/src/machine/machine-dbus.c ++++ b/src/machine/machine-dbus.c +@@ -827,12 +827,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu + return r; + + if (!path_is_absolute(src) || !path_is_normalized(src)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../."); ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized."); + + if (isempty(dest)) + dest = src; + else if (!path_is_absolute(dest) || !path_is_normalized(dest)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../."); ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized."); + + r = bus_verify_polkit_async( + message, diff --git a/debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch b/debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch new file mode 100644 index 0000000..965e925 --- /dev/null +++ b/debian/patches/machine-basic-factor-out-helper-function-to-add-airlocked.patch @@ -0,0 +1,499 @@ +From: Luca Boccassi <luca.boccassi@microsoft.com> +Date: Thu, 13 Aug 2020 14:01:34 +0100 +Subject: machine/basic: factor out helper function to add airlocked mount to + namespace + +(cherry picked from commit 6af52c3a458691b016bedeba34c1e72294a67c81) +--- + src/machine/machine-dbus.c | 214 ++------------------------------------------ + src/shared/mount-util.c | 217 +++++++++++++++++++++++++++++++++++++++++++++ + src/shared/mount-util.h | 2 + + 3 files changed, 227 insertions(+), 206 deletions(-) + +diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c +index 1105008..3c8f4fd 100644 +--- a/src/machine/machine-dbus.c ++++ b/src/machine/machine-dbus.c +@@ -810,17 +810,9 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu + } + + int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error) { +- _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; +- char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p; +- bool mount_slave_created = false, mount_slave_mounted = false, +- mount_tmp_created = false, mount_tmp_mounted = false, +- mount_outside_created = false, mount_outside_mounted = false; +- _cleanup_free_ char *chased_src = NULL; + int read_only, make_file_or_directory; +- const char *dest, *src; ++ const char *dest, *src, *propagate_directory; + Machine *m = userdata; +- struct stat st; +- pid_t child; + uid_t uid; + int r; + +@@ -862,205 +854,15 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu + if (uid != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied."); + +- /* One day, when bind mounting /proc/self/fd/n works across +- * namespace boundaries we should rework this logic to make +- * use of it... */ +- +- p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/"); +- if (laccess(p, F_OK) < 0) +- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points."); +- +- r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, &chased_src, NULL); ++ propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name); ++ r = bind_mount_in_namespace(m->leader, ++ propagate_directory, ++ "/run/host/incoming/", ++ src, dest, read_only, make_file_or_directory); + if (r < 0) +- return sd_bus_error_set_errnof(error, r, "Failed to resolve source path: %m"); +- +- if (lstat(chased_src, &st) < 0) +- return sd_bus_error_set_errnof(error, errno, "Failed to stat() source path: %m"); +- if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safe… */ +- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Source directory can't be a symbolic link"); +- +- /* Our goal is to install a new bind mount into the container, +- possibly read-only. This is irritatingly complex +- unfortunately, currently. +- +- First, we start by creating a private playground in /tmp, +- that we can mount MS_SLAVE. (Which is necessary, since +- MS_MOVE cannot be applied to mounts with MS_SHARED parent +- mounts.) */ +- +- if (!mkdtemp(mount_slave)) +- return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave); +- +- mount_slave_created = true; +- +- r = mount_nofollow_verbose(LOG_DEBUG, mount_slave, mount_slave, NULL, MS_BIND, NULL); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to make bind mount %s: %m", mount_slave); +- goto finish; +- } +- +- mount_slave_mounted = true; +- +- r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_slave, NULL, MS_SLAVE, NULL); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to remount slave %s: %m", mount_slave); +- goto finish; +- } +- +- /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */ +- mount_tmp = strjoina(mount_slave, "/mount"); +- r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to create temporary mount point %s: %m", mount_tmp); +- goto finish; +- } +- +- mount_tmp_created = true; +- +- r = mount_nofollow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to mount %s: %m", chased_src); +- goto finish; +- } +- +- mount_tmp_mounted = true; +- +- /* Third, we remount the new bind mount read-only if requested. */ +- if (read_only) { +- r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to remount read-only %s: %m", mount_tmp); +- goto finish; +- } +- } +- +- /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only +- * right-away. */ +- +- mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX"); +- if (S_ISDIR(st.st_mode)) +- r = mkdtemp(mount_outside) ? 0 : -errno; +- else { +- r = mkostemp_safe(mount_outside); +- safe_close(r); +- } +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Cannot create propagation file or directory %s: %m", mount_outside); +- goto finish; +- } +- +- mount_outside_created = true; +- +- r = mount_nofollow_verbose(LOG_DEBUG, mount_tmp, mount_outside, NULL, MS_MOVE, NULL); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to move %s to %s: %m", mount_tmp, mount_outside); +- goto finish; +- } +- +- mount_outside_mounted = true; +- mount_tmp_mounted = false; +- +- if (S_ISDIR(st.st_mode)) +- (void) rmdir(mount_tmp); +- else +- (void) unlink(mount_tmp); +- mount_tmp_created = false; +- +- (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW); +- mount_slave_mounted = false; +- +- (void) rmdir(mount_slave); +- mount_slave_created = false; +- +- if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) { +- r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); +- goto finish; +- } +- +- r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child); +- if (r < 0) { +- sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); +- goto finish; +- } +- if (r == 0) { +- const char *mount_inside, *q; +- int mntfd; +- +- errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); +- +- q = procfs_file_alloca(m->leader, "ns/mnt"); +- mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); +- if (mntfd < 0) { +- r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); +- goto child_fail; +- } +- +- if (setns(mntfd, CLONE_NEWNS) < 0) { +- r = log_error_errno(errno, "Failed to join namespace of leader: %m"); +- goto child_fail; +- } +- +- if (make_file_or_directory) { +- (void) mkdir_parents(dest, 0755); +- (void) make_mount_point_inode_from_stat(&st, dest, 0700); +- } +- +- mount_inside = strjoina("/run/host/incoming/", basename(mount_outside)); +- r = mount_nofollow_verbose(LOG_ERR, mount_inside, dest, NULL, MS_MOVE, NULL); +- if (r < 0) +- goto child_fail; +- +- _exit(EXIT_SUCCESS); +- +- child_fail: +- (void) write(errno_pipe_fd[1], &r, sizeof(r)); +- errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); +- +- _exit(EXIT_FAILURE); +- } +- +- errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); ++ return sd_bus_error_set_errnof(error, r, "Failed to mount %s on %s in machine's namespace: %m", src, dest); + +- r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0); +- if (r < 0) { +- r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); +- goto finish; +- } +- if (r != EXIT_SUCCESS) { +- if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) +- r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m"); +- else +- r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child failed."); +- goto finish; +- } +- +- r = sd_bus_reply_method_return(message, NULL); +- +-finish: +- if (mount_outside_mounted) +- (void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW); +- if (mount_outside_created) { +- if (S_ISDIR(st.st_mode)) +- (void) rmdir(mount_outside); +- else +- (void) unlink(mount_outside); +- } +- +- if (mount_tmp_mounted) +- (void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW); +- if (mount_tmp_created) { +- if (S_ISDIR(st.st_mode)) +- (void) rmdir(mount_tmp); +- else +- (void) unlink(mount_tmp); +- } +- +- if (mount_slave_mounted) +- (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW); +- if (mount_slave_created) +- (void) rmdir(mount_slave); +- +- return r; ++ return sd_bus_reply_method_return(message, NULL); + } + + int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { +diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c +index b19b384..4cfbb55 100644 +--- a/src/shared/mount-util.c ++++ b/src/shared/mount-util.c +@@ -14,15 +14,18 @@ + #include "fs-util.h" + #include "hashmap.h" + #include "libmount-util.h" ++#include "mkdir.h" + #include "mount-util.h" + #include "mountpoint-util.h" + #include "parse-util.h" + #include "path-util.h" ++#include "process-util.h" + #include "set.h" + #include "stat-util.h" + #include "stdio-util.h" + #include "string-util.h" + #include "strv.h" ++#include "tmpfile-util.h" + + int mount_fd(const char *source, + int target_fd, +@@ -742,3 +745,217 @@ int mount_option_mangle( + + return 0; + } ++ ++int bind_mount_in_namespace( ++ pid_t target, ++ const char *propagate_path, ++ const char *incoming_path, ++ const char *src, ++ const char *dest, ++ bool read_only, ++ bool make_file_or_directory) { ++ ++ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; ++ char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p; ++ bool mount_slave_created = false, mount_slave_mounted = false, ++ mount_tmp_created = false, mount_tmp_mounted = false, ++ mount_outside_created = false, mount_outside_mounted = false; ++ _cleanup_free_ char *chased_src = NULL; ++ struct stat st; ++ pid_t child; ++ int r; ++ ++ assert(target > 0); ++ assert(propagate_path); ++ assert(incoming_path); ++ assert(src); ++ assert(dest); ++ ++ /* One day, when bind mounting /proc/self/fd/n works across ++ * namespace boundaries we should rework this logic to make ++ * use of it... */ ++ ++ p = strjoina(propagate_path, "/"); ++ r = laccess(p, F_OK); ++ if (r < 0) ++ return log_debug_errno(r == -ENOENT ? SYNTHETIC_ERRNO(EOPNOTSUPP) : r, "Target does not allow propagation of mount points"); ++ ++ r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, &chased_src, NULL); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to resolve source path of %s: %m", src); ++ ++ if (lstat(chased_src, &st) < 0) ++ return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", chased_src); ++ if (S_ISLNK(st.st_mode)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safe… */ ++ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Source directory %s can't be a symbolic link", chased_src); ++ ++ /* Our goal is to install a new bind mount into the container, ++ possibly read-only. This is irritatingly complex ++ unfortunately, currently. ++ ++ First, we start by creating a private playground in /tmp, ++ that we can mount MS_SLAVE. (Which is necessary, since ++ MS_MOVE cannot be applied to mounts with MS_SHARED parent ++ mounts.) */ ++ ++ if (!mkdtemp(mount_slave)) ++ return log_debug_errno(errno, "Failed to create playground %s: %m", mount_slave); ++ ++ mount_slave_created = true; ++ ++ r = mount_nofollow_verbose(LOG_DEBUG, mount_slave, mount_slave, NULL, MS_BIND, NULL); ++ if (r < 0) ++ goto finish; ++ ++ mount_slave_mounted = true; ++ ++ r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_slave, NULL, MS_SLAVE, NULL); ++ if (r < 0) ++ goto finish; ++ ++ /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */ ++ mount_tmp = strjoina(mount_slave, "/mount"); ++ r = make_mount_point_inode_from_stat(&st, mount_tmp, 0700); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to create temporary mount point %s: %m", mount_tmp); ++ goto finish; ++ } ++ ++ mount_tmp_created = true; ++ ++ r = mount_nofollow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL); ++ if (r < 0) ++ goto finish; ++ ++ mount_tmp_mounted = true; ++ ++ /* Third, we remount the new bind mount read-only if requested. */ ++ if (read_only) { ++ r = mount_nofollow_verbose(LOG_DEBUG, NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL); ++ if (r < 0) ++ goto finish; ++ } ++ ++ /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only ++ * right-away. */ ++ ++ mount_outside = strjoina(propagate_path, "/XXXXXX"); ++ if (S_ISDIR(st.st_mode)) ++ r = mkdtemp(mount_outside) ? 0 : -errno; ++ else { ++ r = mkostemp_safe(mount_outside); ++ safe_close(r); ++ } ++ if (r < 0) { ++ log_debug_errno(r, "Cannot create propagation file or directory %s: %m", mount_outside); ++ goto finish; ++ } ++ ++ mount_outside_created = true; ++ ++ r = mount_nofollow_verbose(LOG_DEBUG, mount_tmp, mount_outside, NULL, MS_MOVE, NULL); ++ if (r < 0) ++ goto finish; ++ ++ mount_outside_mounted = true; ++ mount_tmp_mounted = false; ++ ++ if (S_ISDIR(st.st_mode)) ++ (void) rmdir(mount_tmp); ++ else ++ (void) unlink(mount_tmp); ++ mount_tmp_created = false; ++ ++ (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW); ++ mount_slave_mounted = false; ++ ++ (void) rmdir(mount_slave); ++ mount_slave_created = false; ++ ++ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) { ++ log_debug_errno(errno, "Failed to create pipe: %m"); ++ goto finish; ++ } ++ ++ r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child); ++ if (r < 0) ++ goto finish; ++ if (r == 0) { ++ const char *mount_inside, *q; ++ int mntfd; ++ ++ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); ++ ++ q = procfs_file_alloca(target, "ns/mnt"); ++ mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); ++ if (mntfd < 0) { ++ r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); ++ goto child_fail; ++ } ++ ++ if (setns(mntfd, CLONE_NEWNS) < 0) { ++ r = log_error_errno(errno, "Failed to join namespace of leader: %m"); ++ goto child_fail; ++ } ++ ++ if (make_file_or_directory) { ++ (void) mkdir_parents(dest, 0755); ++ (void) make_mount_point_inode_from_stat(&st, dest, 0700); ++ } ++ ++ /* Fifth, move the mount to the right place inside */ ++ mount_inside = strjoina(incoming_path, basename(mount_outside)); ++ r = mount_nofollow_verbose(LOG_ERR, mount_inside, dest, NULL, MS_MOVE, NULL); ++ if (r < 0) ++ goto child_fail; ++ ++ _exit(EXIT_SUCCESS); ++ ++ child_fail: ++ (void) write(errno_pipe_fd[1], &r, sizeof(r)); ++ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); ++ ++ _exit(EXIT_FAILURE); ++ } ++ ++ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); ++ ++ r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to wait for child: %m"); ++ goto finish; ++ } ++ if (r != EXIT_SUCCESS) { ++ if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) ++ log_debug_errno(r, "Failed to mount: %m"); ++ else ++ log_debug("Child failed."); ++ goto finish; ++ } ++ ++finish: ++ if (mount_outside_mounted) ++ (void) umount_verbose(LOG_DEBUG, mount_outside, UMOUNT_NOFOLLOW); ++ if (mount_outside_created) { ++ if (S_ISDIR(st.st_mode)) ++ (void) rmdir(mount_outside); ++ else ++ (void) unlink(mount_outside); ++ } ++ ++ if (mount_tmp_mounted) ++ (void) umount_verbose(LOG_DEBUG, mount_tmp, UMOUNT_NOFOLLOW); ++ if (mount_tmp_created) { ++ if (S_ISDIR(st.st_mode)) ++ (void) rmdir(mount_tmp); ++ else ++ (void) unlink(mount_tmp); ++ } ++ ++ if (mount_slave_mounted) ++ (void) umount_verbose(LOG_DEBUG, mount_slave, UMOUNT_NOFOLLOW); ++ if (mount_slave_created) ++ (void) rmdir(mount_slave); ++ ++ return r; ++} +diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h +index 6202008..c3500a0 100644 +--- a/src/shared/mount-util.h ++++ b/src/shared/mount-util.h +@@ -97,3 +97,5 @@ static inline char* umount_and_rmdir_and_free(char *p) { + return NULL; + } + DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free); ++ ++int bind_mount_in_namespace(pid_t target, const char *propagate_path, const char *incoming_path, const char *src, const char *dest, bool read_only, bool make_file_or_directory); diff --git a/debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch b/debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch new file mode 100644 index 0000000..e9cd9dd --- /dev/null +++ b/debian/patches/machine-enter-target-PID-namespace-when-adding-a-live-mou.patch @@ -0,0 +1,105 @@ +From: Luca Boccassi <bluca@debian.org> +Date: Wed, 13 Jan 2021 23:52:00 +0000 +Subject: machine: enter target PID namespace when adding a live mount + +machinectl fails since 21935150a0c42b91a322105f6a9129116bfc8e2e as it's now +mounting onto a file descriptor in a target namespace, without joining the +target's PID namespace. +Note that it's not enough to setns CLONE_NEWPID, but a double-fork is required +as well, as implemented by namespace_fork(). + +Add a test case to TEST-13-NSPAWN to cover this use case. + +(cherry picked from commit 98f654fdeab1e1b6df2be76e29e4ccbb6624898d) +--- + src/shared/mount-util.c | 6 +++--- + test/create-busybox-container | 3 +++ + test/units/testsuite-13.sh | 25 +++++++++++++++++++++++++ + 3 files changed, 31 insertions(+), 3 deletions(-) + +diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c +index 368c5f0..2e374cc 100644 +--- a/src/shared/mount-util.c ++++ b/src/shared/mount-util.c +@@ -757,7 +757,7 @@ int bind_mount_in_namespace( + bool make_file_or_directory) { + + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; +- _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1; ++ _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1, pidns_fd = -1; + char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p; + bool mount_slave_created = false, mount_slave_mounted = false, + mount_tmp_created = false, mount_tmp_mounted = false, +@@ -773,7 +773,7 @@ int bind_mount_in_namespace( + assert(src); + assert(dest); + +- r = namespace_open(target, NULL, &mntns_fd, NULL, NULL, &root_fd); ++ r = namespace_open(target, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m"); + +@@ -898,7 +898,7 @@ int bind_mount_in_namespace( + } + + r = namespace_fork("(sd-bindmnt)", "(sd-bindmnt-inner)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, +- -1, mntns_fd, -1, -1, root_fd, &child); ++ pidns_fd, mntns_fd, -1, -1, root_fd, &child); + if (r < 0) + goto finish; + if (r == 0) { +diff --git a/test/create-busybox-container b/test/create-busybox-container +index 5ded429..b2b7b26 100755 +--- a/test/create-busybox-container ++++ b/test/create-busybox-container +@@ -28,6 +28,9 @@ ln -s busybox "$root/bin/cat" + ln -s busybox "$root/bin/tr" + ln -s busybox "$root/bin/ps" + ln -s busybox "$root/bin/ip" ++ln -s busybox "$root/bin/seq" ++ln -s busybox "$root/bin/sleep" ++ln -s busybox "$root/bin/test" + + mkdir -p "$root/sbin" + cat <<'EOF' >"$root/sbin/init" +diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh +index 969ca4a..1844323 100755 +--- a/test/units/testsuite-13.sh ++++ b/test/units/testsuite-13.sh +@@ -93,6 +93,29 @@ if echo test >> /run/host/os-release; then exit 1; fi + fi + } + ++function check_machinectl_bind { ++ local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;' ++ ++ cat <<EOF > /run/systemd/system/nspawn_machinectl_bind.service ++[Service] ++Type=notify ++ExecStart=systemd-nspawn $SUSE_OPTS -D /testsuite-13.nc-container --notify-ready=no /bin/sh -x -e -c "$_cmd" ++EOF ++ ++ systemctl start nspawn_machinectl_bind.service ++ ++ touch /tmp/marker ++ ++ machinectl bind --mkdir testsuite-13.nc-container /tmp/marker ++ ++ while systemctl show -P SubState nspawn_machinectl_bind.service | grep -q running ++ do ++ sleep 0.1 ++ done ++ ++ return $(systemctl show -P ExecMainStatus nspawn_machinectl_bind.service) ++} ++ + function run { + if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then + printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2 +@@ -186,4 +209,6 @@ for api_vfs_writable in yes no network; do + run yes yes $api_vfs_writable + done + ++check_machinectl_bind ++ + touch /testok diff --git a/debian/patches/machined-varlink-fix-double-free.patch b/debian/patches/machined-varlink-fix-double-free.patch new file mode 100644 index 0000000..80ccaca --- /dev/null +++ b/debian/patches/machined-varlink-fix-double-free.patch @@ -0,0 +1,22 @@ +From: David Tardon <dtardon@redhat.com> +Date: Mon, 2 Aug 2021 13:31:04 +0200 +Subject: machined-varlink: fix double free + +Fixes: #18599 +--- + src/machine/machined-varlink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c +index 2d6c199..8c2e456 100644 +--- a/src/machine/machined-varlink.c ++++ b/src/machine/machined-varlink.c +@@ -297,7 +297,7 @@ static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char + desc = mfree(desc); + + *ret_gid = converted_gid; +- *ret_description = desc; ++ *ret_description = TAKE_PTR(desc); + return 0; + } + diff --git a/debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch b/debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch new file mode 100644 index 0000000..055c598 --- /dev/null +++ b/debian/patches/network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch @@ -0,0 +1,86 @@ +From: "Kevin P. Fleming" <kevin@km6g.us> +Date: Sat, 6 Feb 2021 10:58:43 -0500 +Subject: network: Delay addition of IPv6 Proxy NDP addresses + +Setting of IPv6 Proxy NDP addresses must be done at the same +time as static addresses, static routes, and other link attributes +that must be configured when the link is up. Doing this ensures +that they are reconfigured on the link if the link goes down +and returns to service. + +(cherry picked from commit 12f7469bbe0142d7f360a29ca2b407ce7f5ff096) + +Fixes https://github.com/systemd/systemd-stable/issues/89 + +(cherry picked from commit d5ea028e46673ef627843e90c3d01ebac8fe0e62) +--- + src/network/networkd-address.c | 11 +++++++++++ + src/network/networkd-link.c | 5 ----- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c +index 961b248..ef47af4 100644 +--- a/src/network/networkd-address.c ++++ b/src/network/networkd-address.c +@@ -9,6 +9,7 @@ + #include "netlink-util.h" + #include "networkd-address-pool.h" + #include "networkd-address.h" ++#include "networkd-ipv6-proxy-ndp.h" + #include "networkd-manager.h" + #include "networkd-network.h" + #include "parse-util.h" +@@ -903,6 +904,7 @@ int address_configure( + static int static_address_ready_callback(Address *address) { + Address *a; + Link *link; ++ int r; + + assert(address); + assert(address->link); +@@ -927,6 +929,10 @@ static int static_address_ready_callback(Address *address) { + + link->addresses_ready = true; + ++ r = link_set_ipv6_proxy_ndp_addresses(link); ++ if (r < 0) ++ return r; ++ + return link_set_routes(link); + } + +@@ -1046,6 +1052,11 @@ int link_set_addresses(Link *link) { + if (link->address_messages == 0) { + link->addresses_configured = true; + link->addresses_ready = true; ++ ++ r = link_set_ipv6_proxy_ndp_addresses(link); ++ if (r < 0) ++ return r; ++ + r = link_set_routes(link); + if (r < 0) + return r; +diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c +index 8120343..e8a7223 100644 +--- a/src/network/networkd-link.c ++++ b/src/network/networkd-link.c +@@ -28,7 +28,6 @@ + #include "networkd-dhcp6.h" + #include "networkd-fdb.h" + #include "networkd-ipv4ll.h" +-#include "networkd-ipv6-proxy-ndp.h" + #include "networkd-link-bus.h" + #include "networkd-link.h" + #include "networkd-lldp-tx.h" +@@ -2056,10 +2055,6 @@ int link_configure(Link *link) { + if (r < 0) + return r; + +- r = link_set_ipv6_proxy_ndp_addresses(link); +- if (r < 0) +- return r; +- + r = link_set_mac(link); + if (r < 0) + return r; diff --git a/debian/patches/pkg-config-make-prefix-overridable-again.patch b/debian/patches/pkg-config-make-prefix-overridable-again.patch new file mode 100644 index 0000000..68e50bc --- /dev/null +++ b/debian/patches/pkg-config-make-prefix-overridable-again.patch @@ -0,0 +1,75 @@ +From: Jan Tojnar <jtojnar@gmail.com> +Date: Sat, 2 Jan 2021 02:46:33 +0100 +Subject: pkg-config: make prefix overridable again + +While we don't support prefix being != /usr, and this is hardcoded +all over the place, variables in pkg-config file are expected +to have overridable base directory. + +This is important for at least the following two use cases: + +- Installing projects to non-FHS package-specific prefixes for Nix-style + package managers. Of course, it is then their responsibility + to ensure systemd can find the service files. +- Installing to local path for development purposes. + This is a compromise between running a program from a build directory, + and running it fully installed to system prefix. + +You will not want to write to system prefix in either case. + +For more information, see also +https://www.bassi.io/articles/2018/03/15/pkg-config-and-paths/ + +Fixes https://github.com/systemd/systemd/issues/18082 + +Partially reverts 6e65df89c348242dbd10036abc7dd5e8181cf733 + +(cherry picked from commit 60bce7c6d9606185114df1bdcd5ea100407688b8) +--- + src/core/systemd.pc.in | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in +index f2c0455..b5cc8f9 100644 +--- a/src/core/systemd.pc.in ++++ b/src/core/systemd.pc.in +@@ -26,10 +26,10 @@ systemdsystemunitdir=${systemd_system_unit_dir} + systemd_system_preset_dir=${rootprefix}/lib/systemd/system-preset + systemdsystempresetdir=${systemd_system_preset_dir} + +-systemd_user_unit_dir=/usr/lib/systemd/user ++systemd_user_unit_dir=${prefix}/lib/systemd/user + systemduserunitdir=${systemd_user_unit_dir} + +-systemd_user_preset_dir=/usr/lib/systemd/user-preset ++systemd_user_preset_dir=${prefix}/lib/systemd/user-preset + systemduserpresetdir=${systemd_user_preset_dir} + + systemd_system_conf_dir=${sysconfdir}/systemd/system +@@ -47,7 +47,7 @@ systemduserunitpath=${systemd_user_unit_path} + systemd_system_generator_dir=${root_prefix}/lib/systemd/system-generators + systemdsystemgeneratordir=${systemd_system_generator_dir} + +-systemd_user_generator_dir=/usr/lib/systemd/user-generators ++systemd_user_generator_dir=${prefix}/lib/systemd/user-generators + systemdusergeneratordir=${systemd_user_generator_dir} + + systemd_system_generator_path=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemd_system_generator_dir} +@@ -62,7 +62,7 @@ systemdsleepdir=${systemd_sleep_dir} + systemd_shutdown_dir=${root_prefix}/lib/systemd/system-shutdown + systemdshutdowndir=${systemd_shutdown_dir} + +-tmpfiles_dir=/usr/lib/tmpfiles.d ++tmpfiles_dir=${prefix}/lib/tmpfiles.d + tmpfilesdir=${tmpfiles_dir} + + sysusers_dir=${rootprefix}/lib/sysusers.d +@@ -77,7 +77,7 @@ binfmtdir=${binfmt_dir} + modules_load_dir=${rootprefix}/lib/modules-load.d + modulesloaddir=${modules_load_dir} + +-catalog_dir=/usr/lib/systemd/catalog ++catalog_dir=${prefix}/lib/systemd/catalog + catalogdir=${catalog_dir} + + system_uid_max=@SYSTEM_UID_MAX@ diff --git a/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch b/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch new file mode 100644 index 0000000..67d959c --- /dev/null +++ b/debian/patches/rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch @@ -0,0 +1,128 @@ +From: Lennart Poettering <lennart@poettering.net> +Date: Tue, 26 Jan 2021 16:47:07 +0100 +Subject: rm-rf: fstatat() might fail if containing dir has limited access + mode, patch that too + +(cherry picked from commit 1b55621dabf741dd963f59ac706ea62cd6e3e95c) +(cherry picked from commit ce53b81a600e2162ee86e2f4d202e7f28eceb2c6) +--- + src/basic/rm-rf.c | 82 ++++++++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 66 insertions(+), 16 deletions(-) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index 4c39ce8..2f2ebc3 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -23,13 +23,38 @@ static bool is_physical_fs(const struct statfs *sfs) { + return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); + } + ++static int patch_dirfd_mode( ++ int dfd, ++ mode_t *ret_old_mode) { ++ ++ struct stat st; ++ ++ assert(dfd >= 0); ++ assert(ret_old_mode); ++ ++ if (fstat(dfd, &st) < 0) ++ return -errno; ++ if (!S_ISDIR(st.st_mode)) ++ return -ENOTDIR; ++ if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */ ++ return -EACCES; /* original error */ ++ if (st.st_uid != geteuid()) /* this only works if the UID matches ours */ ++ return -EACCES; ++ ++ if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0) ++ return -errno; ++ ++ *ret_old_mode = st.st_mode; ++ return 0; ++} ++ + static int unlinkat_harder( + int dfd, + const char *filename, + int unlink_flags, + RemoveFlags remove_flags) { + +- struct stat st; ++ mode_t old_mode; + int r; + + /* Like unlinkat(), but tries harder: if we get EACCESS we'll try to set the r/w/x bits on the +@@ -41,22 +66,46 @@ static int unlinkat_harder( + if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD)) + return -errno; + +- if (fstat(dfd, &st) < 0) +- return -errno; +- if (!S_ISDIR(st.st_mode)) +- return -ENOTDIR; +- if (FLAGS_SET(st.st_mode, 0700)) /* Already set? */ +- return -EACCES; /* original error */ +- if (st.st_uid != geteuid()) /* this only works if the UID matches ours */ +- return -EACCES; +- +- if (fchmod(dfd, (st.st_mode | 0700) & 07777) < 0) +- return -errno; ++ r = patch_dirfd_mode(dfd, &old_mode); ++ if (r < 0) ++ return r; + + if (unlinkat(dfd, filename, unlink_flags) < 0) { + r = -errno; + /* Try to restore the original access mode if this didn't work */ +- (void) fchmod(dfd, st.st_mode & 07777); ++ (void) fchmod(dfd, old_mode); ++ return r; ++ } ++ ++ /* If this worked, we won't reset the old mode, since we'll need it for other entries too, and we ++ * should destroy the whole thing */ ++ return 0; ++} ++ ++static int fstatat_harder( ++ int dfd, ++ const char *filename, ++ struct stat *ret, ++ int fstatat_flags, ++ RemoveFlags remove_flags) { ++ ++ mode_t old_mode; ++ int r; ++ ++ /* Like unlink_harder() but does the same for fstatat() */ ++ ++ if (fstatat(dfd, filename, ret, fstatat_flags) >= 0) ++ return 0; ++ if (errno != EACCES || !FLAGS_SET(remove_flags, REMOVE_CHMOD)) ++ return -errno; ++ ++ r = patch_dirfd_mode(dfd, &old_mode); ++ if (r < 0) ++ return r; ++ ++ if (fstatat(dfd, filename, ret, fstatat_flags) < 0) { ++ r = -errno; ++ (void) fchmod(dfd, old_mode); + return r; + } + +@@ -112,9 +161,10 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { + + if (de->d_type == DT_UNKNOWN || + (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { +- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { +- if (ret == 0 && errno != ENOENT) +- ret = -errno; ++ r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags); ++ if (r < 0) { ++ if (ret == 0 && r != -ENOENT) ++ ret = r; + continue; + } + diff --git a/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch b/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch new file mode 100644 index 0000000..66a1ef5 --- /dev/null +++ b/debian/patches/rm-rf-optionally-fsync-after-removing-directory-tree.patch @@ -0,0 +1,39 @@ +From: Lennart Poettering <lennart@poettering.net> +Date: Tue, 5 Oct 2021 10:32:56 +0200 +Subject: rm-rf: optionally fsync() after removing directory tree + +(cherry picked from commit bdfe7ada0d4d66e6d6e65f2822acbb1ec230f9c2) +(cherry picked from commit 2426beacca09d84091759be45b25c88116302184) +(cherry picked from commit 0e180f8e9c25c707b0465ad1b9447a4360f785f1) +(cherry picked from commit 9a9c2220cd3cb61c2de9c482f8ed7fa60807b14a) +--- + src/basic/rm-rf.c | 3 +++ + src/basic/rm-rf.h | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index f1b8445..cf671c2 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -249,6 +249,9 @@ int rm_rf_children( + ret = r; + } + ++ if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0) ++ ret = -errno; ++ + return ret; + } + +diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h +index b0d5b63..2619fc5 100644 +--- a/src/basic/rm-rf.h ++++ b/src/basic/rm-rf.h +@@ -12,6 +12,7 @@ typedef enum RemoveFlags { + REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */ + REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */ + REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete something */ ++ REMOVE_SYNCFS = 1 << 6, /* syncfs() the root of the specified directory after removing everything in it */ + } RemoveFlags; + + int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); diff --git a/debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch b/debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch new file mode 100644 index 0000000..8692c2f --- /dev/null +++ b/debian/patches/rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch @@ -0,0 +1,320 @@ +From: Lennart Poettering <lennart@poettering.net> +Date: Tue, 26 Jan 2021 16:30:06 +0100 +Subject: rm-rf: refactor rm_rf_children(), + split out body of directory iteration loop + +This splits out rm_rf_children_inner() as body of the loop. We can use +that to implement rm_rf_child() for deleting one specific entry in a +directory. + +(cherry picked from commit 1f0fb7d544711248cba34615e43c5a76bc902d74) +(cherry picked from commit ca4a0e7d41f0b2a1fe2f99dbc3763187c16cf7ab) +(cherry picked from commit 85ccac3393e78d4bf2776ffb8c3a1d8a2a909a2a) +(cherry picked from commit a87d7ff1a60fe359978e12eb34224255a8f33e27) +--- + src/basic/rm-rf.c | 223 +++++++++++++++++++++++++++++++----------------------- + src/basic/rm-rf.h | 3 +- + 2 files changed, 131 insertions(+), 95 deletions(-) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index 2f2ebc3..f1b8445 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -19,6 +19,9 @@ + #include "stat-util.h" + #include "string-util.h" + ++/* We treat tmpfs/ramfs + cgroupfs as non-physical file sytems. cgroupfs is similar to tmpfs in a way after ++ * all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it to remove ++ * those again. */ + static bool is_physical_fs(const struct statfs *sfs) { + return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); + } +@@ -112,133 +115,145 @@ static int fstatat_harder( + return 0; + } + +-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { +- _cleanup_closedir_ DIR *d = NULL; +- struct dirent *de; +- int ret = 0, r; +- struct statfs sfs; ++static int rm_rf_children_inner( ++ int fd, ++ const char *fname, ++ int is_dir, ++ RemoveFlags flags, ++ const struct stat *root_dev) { + +- assert(fd >= 0); ++ struct stat st; ++ int r; + +- /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed +- * fd, in all cases, including on failure.. */ ++ assert(fd >= 0); ++ assert(fname); + +- if (!(flags & REMOVE_PHYSICAL)) { ++ if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) { + +- r = fstatfs(fd, &sfs); +- if (r < 0) { +- safe_close(fd); +- return -errno; +- } ++ r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags); ++ if (r < 0) ++ return r; + +- if (is_physical_fs(&sfs)) { +- /* We refuse to clean physical file systems with this call, +- * unless explicitly requested. This is extra paranoia just +- * to be sure we never ever remove non-state data. */ +- _cleanup_free_ char *path = NULL; ++ is_dir = S_ISDIR(st.st_mode); ++ } + +- (void) fd_get_path(fd, &path); +- log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.", +- strna(path)); ++ if (is_dir) { ++ _cleanup_close_ int subdir_fd = -1; ++ int q; + +- safe_close(fd); +- return -EPERM; +- } +- } ++ /* if root_dev is set, remove subdirectories only if device is same */ ++ if (root_dev && st.st_dev != root_dev->st_dev) ++ return 0; + +- d = fdopendir(fd); +- if (!d) { +- safe_close(fd); +- return errno == ENOENT ? 0 : -errno; +- } ++ /* Stop at mount points */ ++ r = fd_is_mount_point(fd, fname, 0); ++ if (r < 0) ++ return r; ++ if (r > 0) ++ return 0; + +- FOREACH_DIRENT_ALL(de, d, return -errno) { +- bool is_dir; +- struct stat st; ++ if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) { + +- if (dot_or_dot_dot(de->d_name)) +- continue; ++ /* This could be a subvolume, try to remove it */ + +- if (de->d_type == DT_UNKNOWN || +- (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { +- r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags); ++ r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r < 0) { +- if (ret == 0 && r != -ENOENT) +- ret = r; +- continue; +- } ++ if (!IN_SET(r, -ENOTTY, -EINVAL)) ++ return r; + +- is_dir = S_ISDIR(st.st_mode); +- } else +- is_dir = de->d_type == DT_DIR; ++ /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ ++ } else ++ /* It was a subvolume, done. */ ++ return 1; ++ } + +- if (is_dir) { +- _cleanup_close_ int subdir_fd = -1; ++ subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); ++ if (subdir_fd < 0) ++ return -errno; + +- /* if root_dev is set, remove subdirectories only if device is same */ +- if (root_dev && st.st_dev != root_dev->st_dev) +- continue; ++ /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type ++ * again for each directory */ ++ q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); + +- subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); +- if (subdir_fd < 0) { +- if (ret == 0 && errno != ENOENT) +- ret = -errno; +- continue; +- } ++ r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags); ++ if (r < 0) ++ return r; ++ if (q < 0) ++ return q; + +- /* Stop at mount points */ +- r = fd_is_mount_point(fd, de->d_name, 0); +- if (r < 0) { +- if (ret == 0 && r != -ENOENT) +- ret = r; ++ return 1; + +- continue; +- } +- if (r > 0) +- continue; ++ } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { ++ r = unlinkat_harder(fd, fname, 0, flags); ++ if (r < 0) ++ return r; + +- if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) { ++ return 1; ++ } + +- /* This could be a subvolume, try to remove it */ ++ return 0; ++} + +- r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); +- if (r < 0) { +- if (!IN_SET(r, -ENOTTY, -EINVAL)) { +- if (ret == 0) +- ret = r; ++int rm_rf_children( ++ int fd, ++ RemoveFlags flags, ++ const struct stat *root_dev) { + +- continue; +- } ++ _cleanup_closedir_ DIR *d = NULL; ++ struct dirent *de; ++ int ret = 0, r; + +- /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ +- } else +- /* It was a subvolume, continue. */ +- continue; +- } ++ assert(fd >= 0); ++ ++ /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed ++ * fd, in all cases, including on failure. */ ++ ++ d = fdopendir(fd); ++ if (!d) { ++ safe_close(fd); ++ return -errno; ++ } + +- /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file +- * system type again for each directory */ +- r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); +- if (r < 0 && ret == 0) +- ret = r; ++ if (!(flags & REMOVE_PHYSICAL)) { ++ struct statfs sfs; + +- r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags); +- if (r < 0 && r != -ENOENT && ret == 0) +- ret = r; ++ if (fstatfs(dirfd(d), &sfs) < 0) ++ return -errno; ++ ++ if (is_physical_fs(&sfs)) { ++ /* We refuse to clean physical file systems with this call, unless explicitly ++ * requested. This is extra paranoia just to be sure we never ever remove non-state ++ * data. */ + +- } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { ++ _cleanup_free_ char *path = NULL; + +- r = unlinkat_harder(fd, de->d_name, 0, flags); +- if (r < 0 && r != -ENOENT && ret == 0) +- ret = r; ++ (void) fd_get_path(fd, &path); ++ return log_error_errno(SYNTHETIC_ERRNO(EPERM), ++ "Attempted to remove disk file system under \"%s\", and we can't allow that.", ++ strna(path)); + } + } ++ ++ FOREACH_DIRENT_ALL(de, d, return -errno) { ++ int is_dir; ++ ++ if (dot_or_dot_dot(de->d_name)) ++ continue; ++ ++ is_dir = ++ de->d_type == DT_UNKNOWN ? -1 : ++ de->d_type == DT_DIR; ++ ++ r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev); ++ if (r < 0 && r != -ENOENT && ret == 0) ++ ret = r; ++ } ++ + return ret; + } + + int rm_rf(const char *path, RemoveFlags flags) { + int fd, r; +- struct statfs s; + + assert(path); + +@@ -283,9 +298,10 @@ int rm_rf(const char *path, RemoveFlags flags) { + if (FLAGS_SET(flags, REMOVE_ROOT)) { + + if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { ++ struct statfs s; ++ + if (statfs(path, &s) < 0) + return -errno; +- + if (is_physical_fs(&s)) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), + "Attempted to remove files from a disk file system under \"%s\", refusing.", +@@ -313,3 +329,22 @@ int rm_rf(const char *path, RemoveFlags flags) { + + return r; + } ++ ++int rm_rf_child(int fd, const char *name, RemoveFlags flags) { ++ ++ /* Removes one specific child of the specified directory */ ++ ++ if (fd < 0) ++ return -EBADF; ++ ++ if (!filename_is_valid(name)) ++ return -EINVAL; ++ ++ if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */ ++ return -EINVAL; ++ ++ if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) ++ return -EINVAL; ++ ++ return rm_rf_children_inner(fd, name, -1, flags, NULL); ++} +diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h +index ec56232..b0d5b63 100644 +--- a/src/basic/rm-rf.h ++++ b/src/basic/rm-rf.h +@@ -14,7 +14,8 @@ typedef enum RemoveFlags { + REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete something */ + } RemoveFlags; + +-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); ++int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); ++int rm_rf_child(int fd, const char *name, RemoveFlags flags); + int rm_rf(const char *path, RemoveFlags flags); + + /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ diff --git a/debian/patches/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch b/debian/patches/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch new file mode 100644 index 0000000..d338f49 --- /dev/null +++ b/debian/patches/rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch @@ -0,0 +1,41 @@ +From: Vincent Pelletier <plr.vincent@gmail.com> +Date: Sat, 27 Feb 2021 00:17:06 +0000 +Subject: rules: Move ID_SMARTCARD_READER definition to a <70 configuration. + +70-uaccess.rules sets the uaccess tag on devices with ID_SMARTCARD_READER +set, but it is set in 99-systemd.rules . +Move this to a 60-*.rules which already matches USB CCID class, factorising +the matching, so 70-uaccess.rules sets up these devices as expected. + +(cherry picked from commit dbdcd51f78bde5e9033d98d61bbb750c868bde9d) +--- + rules.d/60-fido-id.rules | 3 ++- + rules.d/99-systemd.rules.in | 1 - + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/rules.d/60-fido-id.rules b/rules.d/60-fido-id.rules +index c7d5d2f..48c259e 100644 +--- a/rules.d/60-fido-id.rules ++++ b/rules.d/60-fido-id.rules +@@ -7,7 +7,8 @@ SUBSYSTEM=="hidraw", IMPORT{program}="fido_id" + # Tag any form of security token as such + ENV{ID_SECURITY_TOKEN}=="1", TAG+="security-device" + ++SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1" + # Tag any CCID device (i.e. Smartcard Reader) as security token +-SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="0b", TAG+="security-device" ++ENV{ID_SMARTCARD_READER}=="1", TAG+="security-device" + + LABEL="fido_id_end" +diff --git a/rules.d/99-systemd.rules.in b/rules.d/99-systemd.rules.in +index 7c22eef..0abca6e 100644 +--- a/rules.d/99-systemd.rules.in ++++ b/rules.d/99-systemd.rules.in +@@ -49,7 +49,6 @@ SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsys + SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/bluetooth/devices/%k", \ + ENV{SYSTEMD_WANTS}+="bluetooth.target", ENV{SYSTEMD_USER_WANTS}+="bluetooth.target" + +-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0b????:*", ENV{ID_SMARTCARD_READER}="1" + ENV{ID_SMARTCARD_READER}=="?*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="smartcard.target", ENV{SYSTEMD_USER_WANTS}+="smartcard.target" + SUBSYSTEM=="sound", KERNEL=="controlC*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sound.target", ENV{SYSTEMD_USER_WANTS}+="sound.target" + diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..f81d7b4 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,66 @@ +Add-helper-for-case-independent-string-equality-checks.patch +localed-Run-locale-gen-if-available-to-generate-missing-l.patch +core-fix-mtime-calculation-of-dropin-files.patch +analyze-slightly-reword-PrivateTmp-message.patch +rules-Move-ID_SMARTCARD_READER-definition-to-a-70-configu.patch +table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch +pkg-config-make-prefix-overridable-again.patch +LoadCredentials-do-not-assert-on-invalid-syntax.patch +network-Delay-addition-of-IPv6-Proxy-NDP-addresses.patch +unit-name-generate-a-clear-error-code-when-convertin.patch +basic-unit-name-do-not-use-strdupa-on-a-path.patch +basic-unit-name-adjust-comments.patch +udevadm-trigger-do-not-return-immediately-on-EACCES.patch +btrfs-util-add-helper-that-abstracts-might-be-btrfs-subvo.patch +rm-rf-fstatat-might-fail-if-containing-dir-has-limited-ac.patch +rm-rf-refactor-rm_rf_children-split-out-body-of-directory.patch +rm-rf-optionally-fsync-after-removing-directory-tree.patch +tmpfiles-st-may-have-been-used-uninitialized.patch +shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch +shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch +shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch +basic-add-make_mount_point_inode-helper.patch +machine-basic-factor-out-helper-function-to-add-airlocked.patch +machine-adjust-error-message-to-use-normalized-instead-of.patch +shared-mount-util-use-namespace_fork-utils.patch +machine-enter-target-PID-namespace-when-adding-a-live-mou.patch +Drop-bundled-copy-of-linux-if_arp.h.patch +virt-Support-detection-for-ARM64-Hyper-V-guests.patch +virt-Fix-the-detection-for-Hyper-V-VMs.patch +virt-detect-OpenStack-Nova-instance.patch +ata_id-Fixed-getting-Response-Code-from-SCSI-Sense-Data-2.patch +udev-always-create-device-symlinks-for-USB-disks.patch +Revert-udev-do-not-execute-hwdb-builtin-import-twice-or-t.patch +udev-first-set-properties-based-on-usb-subsystem.patch +logind-fix-getting-property-OnExternalPower-via-D-Bus.patch +coredump-do-not-allow-user-to-access-coredumps-with-chang.patch +time-util-fix-buffer-over-run.patch +machined-varlink-fix-double-free.patch +Always-free-deserialized_subscribed-on-reload.patch +shared-calendarspec-abort-calculation-after-1000-iteratio.patch +shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch +debian/Use-Debian-specific-config-files.patch +debian/Bring-tmpfiles.d-tmp.conf-in-line-with-Debian-defaul.patch +debian/Make-run-lock-tmpfs-an-API-fs.patch +debian/Add-support-for-TuxOnIce-hibernation.patch +debian/Re-enable-journal-forwarding-to-syslog.patch +debian/Don-t-enable-audit-by-default.patch +debian/Only-start-logind-if-dbus-is-installed.patch +debian/fsckd-daemon-for-inter-fsckd-communication.patch +debian/Skip-filesystem-check-if-already-done-by-the-initram.patch +debian/Revert-core-one-step-back-again-for-nspawn-we-actual.patch +debian/Revert-core-set-RLIMIT_CORE-to-unlimited-by-default.patch +debian/Let-graphical-session-pre.target-be-manually-started.patch +debian/Add-env-variable-for-machine-ID-path.patch +debian/Drop-seccomp-system-call-filter-for-udev.patch +debian/deny-list-upstream-test-25.patch +debian/deny-list-upstream-test-02-ppc64el.patch +debian/udev-drop-SystemCallArchitectures-native-from-systemd-ude.patch +debian/Keep-journal-files-compatible-with-older-versions.patch +debian/Move-sysusers.d-sysctl.d-binfmt.d-modules-load.d-back-to-.patch +debian/systemctl-do-not-shutdown-immediately-on-scheduled-shutdo.patch +debian/test-disable-DnsmasqClientTest.test_resolved_etc_hosts-in.patch +debian/Downgrade-a-couple-of-warnings-to-debug.patch +debian/Revert-udev-fix-memleak.patch +debian/Revert-udev-link_update-should-fail-if-the-entry-in-symli.patch +debian/Revert-udev-make-algorithm-that-selects-highest-priority-.patch diff --git a/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch b/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch new file mode 100644 index 0000000..49562a7 --- /dev/null +++ b/debian/patches/shared-calendarspec-abort-calculation-after-1000-iteratio.patch @@ -0,0 +1,55 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Sun, 21 Mar 2021 20:59:32 +0100 +Subject: shared/calendarspec: abort calculation after 1000 iterations + +We have a bug where we seem to enter an infinite loop when running in the +Europe/Dublin timezone. The timezone is "special" because it has negative SAVE +values. The handling of this should obviously be fixed, but let's use a +belt-and-suspenders approach, and gracefully fail if we fail to find an answer +within a specific number of attempts. The code in this function is rather +complex, and it's hard to rule out another bug in the future. + +(cherry picked from commit 169615c9a8cdc54d748d4dfc8279be9b3c2bec44) +--- + src/shared/calendarspec.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c +index 7162592..80acc57 100644 +--- a/src/shared/calendarspec.c ++++ b/src/shared/calendarspec.c +@@ -1211,6 +1211,10 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { + return (weekdays_bits & (1 << k)); + } + ++/* A safety valve: if we get stuck in the calculation, return an error. ++ * C.f. https://bugzilla.redhat.com/show_bug.cgi?id=1941335. */ ++#define MAX_CALENDAR_ITERATIONS 1000 ++ + static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + struct tm c; + int tm_usec; +@@ -1224,7 +1228,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + c = *tm; + tm_usec = *usec; + +- for (;;) { ++ for (unsigned iteration = 0; iteration < MAX_CALENDAR_ITERATIONS; iteration++) { + /* Normalize the current date */ + (void) mktime_or_timegm(&c, spec->utc); + c.tm_isdst = spec->dst; +@@ -1321,6 +1325,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { + *usec = tm_usec; + return 0; + } ++ ++ /* It seems we entered an infinite loop. Let's gracefully return an error instead of hanging or ++ * aborting. This code is also exercised when timers.target is brought up during early boot, so ++ * aborting here is problematic and hard to diagnose for users. */ ++ _cleanup_free_ char *s = NULL; ++ (void) calendar_spec_to_string(spec, &s); ++ return log_warning_errno(SYNTHETIC_ERRNO(EDEADLK), ++ "Infinite loop in calendar calculation: %s", strna(s)); + } + + static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) { diff --git a/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch b/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch new file mode 100644 index 0000000..9503c1e --- /dev/null +++ b/debian/patches/shared-calendarspec-when-mktime-moves-us-backwards-jump-f.patch @@ -0,0 +1,105 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Mon, 22 Mar 2021 12:51:47 +0100 +Subject: shared/calendarspec: when mktime() moves us backwards, jump forward +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +When trying to calculate the next firing of 'Sun *-*-* 01:00:00', we'd fall +into an infinite loop, because mktime() moves us "backwards": + +Before this patch: +tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00 +tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00 +tm_within_bounds: good=0 2021-03-29 01:00:00 → 2021-03-29 00:00:00 +... + +We rely on mktime() normalizing the time. The man page does not say that it'll +move the time forward, but our algorithm relies on this. So let's catch this +case explicitly. + +With this patch: +$ TZ=Europe/Dublin faketime 2021-03-21 build/systemd-analyze calendar --iterations=5 'Sun *-*-* 01:00:00' +Normalized form: Sun *-*-* 01:00:00 + Next elapse: Sun 2021-03-21 01:00:00 GMT + (in UTC): Sun 2021-03-21 01:00:00 UTC + From now: 59min left + Iter. #2: Sun 2021-04-04 01:00:00 IST + (in UTC): Sun 2021-04-04 00:00:00 UTC + From now: 1 weeks 6 days left <---- note the 2 week jump here + Iter. #3: Sun 2021-04-11 01:00:00 IST + (in UTC): Sun 2021-04-11 00:00:00 UTC + From now: 2 weeks 6 days left + Iter. #4: Sun 2021-04-18 01:00:00 IST + (in UTC): Sun 2021-04-18 00:00:00 UTC + From now: 3 weeks 6 days left + Iter. #5: Sun 2021-04-25 01:00:00 IST + (in UTC): Sun 2021-04-25 00:00:00 UTC + From now: 1 months 4 days left + +Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1941335. + +(cherry picked from commit 129cb6e249bef30dc33e08f98f0b27a6de976f6f) +--- + src/shared/calendarspec.c | 19 +++++++++++-------- + src/test/test-calendarspec.c | 3 +++ + test/test-functions | 1 + + 3 files changed, 15 insertions(+), 8 deletions(-) + +diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c +index 80acc57..c8d97c3 100644 +--- a/src/shared/calendarspec.c ++++ b/src/shared/calendarspec.c +@@ -1185,15 +1185,18 @@ static int tm_within_bounds(struct tm *tm, bool utc) { + return negative_errno(); + + /* Did any normalization take place? If so, it was out of bounds before */ +- bool good = t.tm_year == tm->tm_year && +- t.tm_mon == tm->tm_mon && +- t.tm_mday == tm->tm_mday && +- t.tm_hour == tm->tm_hour && +- t.tm_min == tm->tm_min && +- t.tm_sec == tm->tm_sec; +- if (!good) ++ int cmp = CMP(t.tm_year, tm->tm_year) ?: ++ CMP(t.tm_mon, tm->tm_mon) ?: ++ CMP(t.tm_mday, tm->tm_mday) ?: ++ CMP(t.tm_hour, tm->tm_hour) ?: ++ CMP(t.tm_min, tm->tm_min) ?: ++ CMP(t.tm_sec, tm->tm_sec); ++ ++ if (cmp < 0) ++ return -EDEADLK; /* Refuse to go backward */ ++ if (cmp > 0) + *tm = t; +- return good; ++ return cmp == 0; + } + + static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { +diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c +index e0b7f22..1b04186 100644 +--- a/src/test/test-calendarspec.c ++++ b/src/test/test-calendarspec.c +@@ -218,6 +218,9 @@ int main(int argc, char* argv[]) { + // Confirm that timezones in the Spec work regardless of current timezone + test_next("2017-09-09 20:42:00 Pacific/Auckland", "", 12345, 1504946520000000); + test_next("2017-09-09 20:42:00 Pacific/Auckland", "EET", 12345, 1504946520000000); ++ /* Check that we don't start looping if mktime() moves us backwards */ ++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "", 1616412478000000, 1617494400000000); ++ test_next("Sun *-*-* 01:00:00 Europe/Dublin", "IST", 1616412478000000, 1617494400000000); + + assert_se(calendar_spec_from_string("test", &c) < 0); + assert_se(calendar_spec_from_string(" utc", &c) < 0); +diff --git a/test/test-functions b/test/test-functions +index 52b52bf..beaf4fa 100644 +--- a/test/test-functions ++++ b/test/test-functions +@@ -1120,6 +1120,7 @@ install_zoneinfo() { + inst_any /usr/share/zoneinfo/Asia/Vladivostok + inst_any /usr/share/zoneinfo/Australia/Sydney + inst_any /usr/share/zoneinfo/Europe/Berlin ++ inst_any /usr/share/zoneinfo/Europe/Dublin + inst_any /usr/share/zoneinfo/Europe/Kiev + inst_any /usr/share/zoneinfo/Pacific/Auckland + inst_any /usr/share/zoneinfo/Pacific/Honolulu diff --git a/debian/patches/shared-mount-util-use-namespace_fork-utils.patch b/debian/patches/shared-mount-util-use-namespace_fork-utils.patch new file mode 100644 index 0000000..f870a3e --- /dev/null +++ b/debian/patches/shared-mount-util-use-namespace_fork-utils.patch @@ -0,0 +1,92 @@ +From: Luca Boccassi <luca.boccassi@microsoft.com> +Date: Thu, 13 Aug 2020 14:47:01 +0100 +Subject: shared/mount-util: use namespace_fork utils + +(cherry picked from commit 2338a175fdec3859eab03115ca82a0d58453f5d7) +--- + src/shared/mount-util.c | 40 ++++++++++++++++++++++++---------------- + 1 file changed, 24 insertions(+), 16 deletions(-) + +diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c +index 4cfbb55..368c5f0 100644 +--- a/src/shared/mount-util.c ++++ b/src/shared/mount-util.c +@@ -17,6 +17,7 @@ + #include "mkdir.h" + #include "mount-util.h" + #include "mountpoint-util.h" ++#include "namespace-util.h" + #include "parse-util.h" + #include "path-util.h" + #include "process-util.h" +@@ -756,12 +757,13 @@ int bind_mount_in_namespace( + bool make_file_or_directory) { + + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; ++ _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1; + char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p; + bool mount_slave_created = false, mount_slave_mounted = false, + mount_tmp_created = false, mount_tmp_mounted = false, + mount_outside_created = false, mount_outside_mounted = false; + _cleanup_free_ char *chased_src = NULL; +- struct stat st; ++ struct stat st, self_mntns_st; + pid_t child; + int r; + +@@ -771,6 +773,24 @@ int bind_mount_in_namespace( + assert(src); + assert(dest); + ++ r = namespace_open(target, NULL, &mntns_fd, NULL, NULL, &root_fd); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m"); ++ ++ if (fstat(mntns_fd, &st) < 0) ++ return log_debug_errno(errno, "Failed to fstat mount namespace FD of target process: %m"); ++ ++ r = namespace_open(0, NULL, &self_mntns_fd, NULL, NULL, NULL); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to retrieve FDs of systemd's namespace: %m"); ++ ++ if (fstat(self_mntns_fd, &self_mntns_st) < 0) ++ return log_debug_errno(errno, "Failed to fstat mount namespace FD of systemd: %m"); ++ ++ /* We can't add new mounts at runtime if the process wasn't started in a namespace */ ++ if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev) ++ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace"); ++ + /* One day, when bind mounting /proc/self/fd/n works across + * namespace boundaries we should rework this logic to make + * use of it... */ +@@ -877,27 +897,15 @@ int bind_mount_in_namespace( + goto finish; + } + +- r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child); ++ r = namespace_fork("(sd-bindmnt)", "(sd-bindmnt-inner)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, ++ -1, mntns_fd, -1, -1, root_fd, &child); + if (r < 0) + goto finish; + if (r == 0) { +- const char *mount_inside, *q; +- int mntfd; ++ const char *mount_inside; + + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + +- q = procfs_file_alloca(target, "ns/mnt"); +- mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); +- if (mntfd < 0) { +- r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); +- goto child_fail; +- } +- +- if (setns(mntfd, CLONE_NEWNS) < 0) { +- r = log_error_errno(errno, "Failed to join namespace of leader: %m"); +- goto child_fail; +- } +- + if (make_file_or_directory) { + (void) mkdir_parents(dest, 0755); + (void) make_mount_point_inode_from_stat(&st, dest, 0700); diff --git a/debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch b/debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch new file mode 100644 index 0000000..5ab54bf --- /dev/null +++ b/debian/patches/shared-rm-rf-loop-over-nested-directories-instead-of-inst.patch @@ -0,0 +1,264 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Tue, 30 Nov 2021 22:29:05 +0100 +Subject: shared/rm-rf: loop over nested directories instead of instead of + recursing + +To remove directory structures, we need to remove the innermost items first, +and then recursively remove higher-level directories. We would recursively +descend into directories and invoke rm_rf_children and rm_rm_children_inner. +This is problematic when too many directories are nested. + +Instead, let's create a "TODO" queue. In the the queue, for each level we +hold the DIR* object we were working on, and the name of the directory. This +allows us to leave a partially-processed directory, and restart the removal +loop one level down. When done with the inner directory, we use the name to +unlinkat() it from the parent, and proceed with the removal of other items. + +Because the nesting is increased by one level, it is best to view this patch +with -b/--ignore-space-change. + +This fixes CVE-2021-3997, https://bugzilla.redhat.com/show_bug.cgi?id=2024639. +The issue was reported and patches reviewed by Qualys Team. +Mauro Matteo Cascella and Riccardo Schirone from Red Hat handled the disclosure. + +(cherry picked from commit 5b1cf7a9be37e20133c0208005274ce4a5b5c6a1) +(cherry picked from commit 911516e1614e435755814ada5fc6064fa107a105) +(cherry picked from commit 6a28f8b55904c818b25e4db2e1511faac79fd471) +(cherry picked from commit c752f27b7647c99b4a17477c99d84fd8c950ddf0) +(cherry picked from commit 921810ea23357988ce67f49190f43abef1788a9c) +--- + src/basic/rm-rf.c | 160 ++++++++++++++++++++++++++++++++++++++---------------- + 1 file changed, 113 insertions(+), 47 deletions(-) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index 2901307..77ffed9 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -115,12 +115,13 @@ static int fstatat_harder( + return 0; + } + +-static int rm_rf_children_inner( ++static int rm_rf_inner_child( + int fd, + const char *fname, + int is_dir, + RemoveFlags flags, +- const struct stat *root_dev) { ++ const struct stat *root_dev, ++ bool allow_recursion) { + + struct stat st; + int r, q = 0; +@@ -140,9 +141,7 @@ static int rm_rf_children_inner( + } + + if (is_dir) { +- _cleanup_close_ int subdir_fd = -1; +- +- /* if root_dev is set, remove subdirectories only if device is same */ ++ /* If root_dev is set, remove subdirectories only if device is same */ + if (root_dev && st.st_dev != root_dev->st_dev) + return 0; + +@@ -154,7 +153,6 @@ static int rm_rf_children_inner( + return 0; + + if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) { +- + /* This could be a subvolume, try to remove it */ + + r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); +@@ -168,13 +166,16 @@ static int rm_rf_children_inner( + return 1; + } + +- subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); ++ if (!allow_recursion) ++ return -EISDIR; ++ ++ int subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (subdir_fd < 0) + return -errno; + + /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type + * again for each directory */ +- q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); ++ q = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev); + + } else if (flags & REMOVE_ONLY_DIRECTORIES) + return 0; +@@ -187,63 +188,128 @@ static int rm_rf_children_inner( + return 1; + } + ++typedef struct TodoEntry { ++ DIR *dir; /* A directory that we were operating on. */ ++ char *dirname; /* The filename of that directory itself. */ ++} TodoEntry; ++ ++static void free_todo_entries(TodoEntry **todos) { ++ for (TodoEntry *x = *todos; x && x->dir; x++) { ++ closedir(x->dir); ++ free(x->dirname); ++ } ++ ++ freep(todos); ++} ++ + int rm_rf_children( + int fd, + RemoveFlags flags, + const struct stat *root_dev) { + +- _cleanup_closedir_ DIR *d = NULL; +- struct dirent *de; ++ _cleanup_(free_todo_entries) TodoEntry *todos = NULL; ++ size_t n_todo = 0, n_todo_alloc = 0; ++ _cleanup_free_ char *dirname = NULL; /* Set when we are recursing and want to delete ourselves */ + int ret = 0, r; + +- assert(fd >= 0); ++ /* Return the first error we run into, but nevertheless try to go on. ++ * The passed fd is closed in all cases, including on failure. */ ++ ++ for (;;) { /* This loop corresponds to the directory nesting level. */ ++ _cleanup_closedir_ DIR *d = NULL; ++ ++ if (n_todo > 0) { ++ /* We know that we are in recursion here, because n_todo is set. ++ * We need to remove the inner directory we were operating on. */ ++ assert(dirname); ++ r = unlinkat_harder(dirfd(todos[n_todo-1].dir), dirname, AT_REMOVEDIR, flags); ++ if (r < 0 && r != -ENOENT && ret == 0) ++ ret = r; ++ dirname = mfree(dirname); ++ ++ /* And now let's back out one level up */ ++ n_todo --; ++ d = TAKE_PTR(todos[n_todo].dir); ++ dirname = TAKE_PTR(todos[n_todo].dirname); ++ ++ assert(d); ++ fd = dirfd(d); /* Retrieve the file descriptor from the DIR object */ ++ assert(fd >= 0); ++ } else { ++ next_fd: ++ assert(fd >= 0); ++ d = fdopendir(fd); ++ if (!d) { ++ safe_close(fd); ++ return -errno; ++ } ++ fd = dirfd(d); /* We donated the fd to fdopendir(). Let's make sure we sure we have ++ * the right descriptor even if it were to internally invalidate the ++ * one we passed. */ ++ ++ if (!(flags & REMOVE_PHYSICAL)) { ++ struct statfs sfs; ++ ++ if (fstatfs(fd, &sfs) < 0) ++ return -errno; ++ ++ if (is_physical_fs(&sfs)) { ++ /* We refuse to clean physical file systems with this call, unless ++ * explicitly requested. This is extra paranoia just to be sure we ++ * never ever remove non-state data. */ ++ ++ _cleanup_free_ char *path = NULL; ++ ++ (void) fd_get_path(fd, &path); ++ return log_error_errno(SYNTHETIC_ERRNO(EPERM), ++ "Attempted to remove disk file system under \"%s\", and we can't allow that.", ++ strna(path)); ++ } ++ } ++ } + +- /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed +- * fd, in all cases, including on failure. */ ++ struct dirent *de; ++ FOREACH_DIRENT_ALL(de, d, return -errno) { ++ int is_dir; + +- d = fdopendir(fd); +- if (!d) { +- safe_close(fd); +- return -errno; +- } ++ if (dot_or_dot_dot(de->d_name)) ++ continue; + +- if (!(flags & REMOVE_PHYSICAL)) { +- struct statfs sfs; ++ is_dir = de->d_type == DT_UNKNOWN ? -1 : de->d_type == DT_DIR; + +- if (fstatfs(dirfd(d), &sfs) < 0) +- return -errno; ++ r = rm_rf_inner_child(fd, de->d_name, is_dir, flags, root_dev, false); ++ if (r == -EISDIR) { ++ /* Push the current working state onto the todo list */ + +- if (is_physical_fs(&sfs)) { +- /* We refuse to clean physical file systems with this call, unless explicitly +- * requested. This is extra paranoia just to be sure we never ever remove non-state +- * data. */ ++ if (!GREEDY_REALLOC0(todos, n_todo_alloc, n_todo + 2)) ++ return log_oom(); + +- _cleanup_free_ char *path = NULL; ++ _cleanup_free_ char *newdirname = strdup(de->d_name); ++ if (!newdirname) ++ return log_oom(); + +- (void) fd_get_path(fd, &path); +- return log_error_errno(SYNTHETIC_ERRNO(EPERM), +- "Attempted to remove disk file system under \"%s\", and we can't allow that.", +- strna(path)); +- } +- } ++ int newfd = openat(fd, de->d_name, ++ O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); ++ if (newfd >= 0) { ++ todos[n_todo++] = (TodoEntry) { TAKE_PTR(d), TAKE_PTR(dirname) }; ++ fd = newfd; ++ dirname = TAKE_PTR(newdirname); + +- FOREACH_DIRENT_ALL(de, d, return -errno) { +- int is_dir; ++ goto next_fd; + +- if (dot_or_dot_dot(de->d_name)) +- continue; ++ } else if (errno != -ENOENT && ret == 0) ++ ret = -errno; + +- is_dir = +- de->d_type == DT_UNKNOWN ? -1 : +- de->d_type == DT_DIR; ++ } else if (r < 0 && r != -ENOENT && ret == 0) ++ ret = r; ++ } + +- r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev); +- if (r < 0 && r != -ENOENT && ret == 0) +- ret = r; +- } ++ if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(fd) < 0 && ret >= 0) ++ ret = -errno; + +- if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0) +- ret = -errno; ++ if (n_todo == 0) ++ break; ++ } + + return ret; + } +@@ -336,5 +402,5 @@ int rm_rf_child(int fd, const char *name, RemoveFlags flags) { + if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) + return -EINVAL; + +- return rm_rf_children_inner(fd, name, -1, flags, NULL); ++ return rm_rf_inner_child(fd, name, -1, flags, NULL, true); + } diff --git a/debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch b/debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch new file mode 100644 index 0000000..6f1d1c0 --- /dev/null +++ b/debian/patches/shared-rm_rf-refactor-rm_rf-to-shorten-code-a-bit.patch @@ -0,0 +1,99 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Tue, 23 Nov 2021 16:56:42 +0100 +Subject: shared/rm_rf: refactor rm_rf() to shorten code a bit + +(cherry picked from commit 84ced330020c0bae57bd4628f1f44eec91304e69) +(cherry picked from commit 664529efa9431edc043126013ea54e6c399ae2d3) +(cherry picked from commit 811b137d6137cc3e8932599e6ef9254ba43ff5eb) +(cherry picked from commit 39a53d4f1445a8981efd0adcc1734dfad46647c5) +(cherry picked from commit aaad978868bd6ac84d463a94357ddcbc43b24248) +--- + src/basic/rm-rf.c | 54 ++++++++++++++++++++++++------------------------------ + 1 file changed, 24 insertions(+), 30 deletions(-) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index 343a097..2901307 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -249,7 +249,7 @@ int rm_rf_children( + } + + int rm_rf(const char *path, RemoveFlags flags) { +- int fd, r; ++ int fd, r, q = 0; + + assert(path); + +@@ -281,49 +281,43 @@ int rm_rf(const char *path, RemoveFlags flags) { + } + + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); +- if (fd < 0) { ++ if (fd >= 0) { ++ /* We have a dir */ ++ r = rm_rf_children(fd, flags, NULL); ++ ++ if (FLAGS_SET(flags, REMOVE_ROOT) && rmdir(path) < 0) ++ q = -errno; ++ } else { + if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT) + return 0; + + if (!IN_SET(errno, ENOTDIR, ELOOP)) + return -errno; + +- if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES)) ++ if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES) || !FLAGS_SET(flags, REMOVE_ROOT)) + return 0; + +- if (FLAGS_SET(flags, REMOVE_ROOT)) { +- +- if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { +- struct statfs s; +- +- if (statfs(path, &s) < 0) +- return -errno; +- if (is_physical_fs(&s)) +- return log_error_errno(SYNTHETIC_ERRNO(EPERM), +- "Attempted to remove files from a disk file system under \"%s\", refusing.", +- path); +- } +- +- if (unlink(path) < 0) { +- if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT) +- return 0; ++ if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { ++ struct statfs s; + ++ if (statfs(path, &s) < 0) + return -errno; +- } ++ if (is_physical_fs(&s)) ++ return log_error_errno(SYNTHETIC_ERRNO(EPERM), ++ "Attempted to remove files from a disk file system under \"%s\", refusing.", ++ path); + } + +- return 0; ++ r = 0; ++ if (unlink(path) < 0) ++ q = -errno; + } + +- r = rm_rf_children(fd, flags, NULL); +- +- if (FLAGS_SET(flags, REMOVE_ROOT) && +- rmdir(path) < 0 && +- r >= 0 && +- (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT)) +- r = -errno; +- +- return r; ++ if (r < 0) ++ return r; ++ if (q < 0 && (q != -ENOENT || !FLAGS_SET(flags, REMOVE_MISSING_OK))) ++ return q; ++ return 0; + } + + int rm_rf_child(int fd, const char *name, RemoveFlags flags) { diff --git a/debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch b/debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch new file mode 100644 index 0000000..7a7f85a --- /dev/null +++ b/debian/patches/shared-rm_rf-refactor-rm_rf_children_inner-to-shorten-cod.patch @@ -0,0 +1,66 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Tue, 23 Nov 2021 15:55:45 +0100 +Subject: shared/rm_rf: refactor rm_rf_children_inner() to shorten code a bit + +(cherry picked from commit 3bac86abfa1b1720180840ffb9d06b3d54841c11) +(cherry picked from commit 47741ff9eae6311a03e4d3d837128191826a4a3a) +(cherry picked from commit 89395b63f04f1acc0db533c32637ea20379f97c0) +(cherry picked from commit 3976f244990aa1210ebe018647f32ab060e1c3d3) +(cherry picked from commit 988e43630bb7592947c75fe530a6f7dfebc00c4f) +--- + src/basic/rm-rf.c | 27 +++++++++------------------ + 1 file changed, 9 insertions(+), 18 deletions(-) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index a78aa4f..343a097 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -123,7 +123,7 @@ static int rm_rf_children_inner( + const struct stat *root_dev) { + + struct stat st; +- int r; ++ int r, q = 0; + + assert(fd >= 0); + assert(fname); +@@ -141,7 +141,6 @@ static int rm_rf_children_inner( + + if (is_dir) { + _cleanup_close_ int subdir_fd = -1; +- int q; + + /* if root_dev is set, remove subdirectories only if device is same */ + if (root_dev && st.st_dev != root_dev->st_dev) +@@ -177,23 +176,15 @@ static int rm_rf_children_inner( + * again for each directory */ + q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); + +- r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags); +- if (r < 0) +- return r; +- if (q < 0) +- return q; +- +- return 1; +- +- } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { +- r = unlinkat_harder(fd, fname, 0, flags); +- if (r < 0) +- return r; +- +- return 1; +- } ++ } else if (flags & REMOVE_ONLY_DIRECTORIES) ++ return 0; + +- return 0; ++ r = unlinkat_harder(fd, fname, is_dir ? AT_REMOVEDIR : 0, flags); ++ if (r < 0) ++ return r; ++ if (q < 0) ++ return q; ++ return 1; + } + + int rm_rf_children( diff --git a/debian/patches/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch b/debian/patches/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch new file mode 100644 index 0000000..fea1aec --- /dev/null +++ b/debian/patches/table-drop-trailing-white-spaces-of-the-last-cell-in-row.patch @@ -0,0 +1,167 @@ +From: Yu Watanabe <watanabe.yu+github@gmail.com> +Date: Tue, 2 Feb 2021 01:47:58 +0900 +Subject: table: drop trailing white spaces of the last cell in row + +Fixes #18415. + +(cherry picked from commit 71894e18313e41a72cecdc77fea5037f95d6903f) +--- + src/shared/format-table.c | 6 ++++++ + src/test/test-format-table.c | 40 ++++++++++++++++++++-------------------- + 2 files changed, 26 insertions(+), 20 deletions(-) + +diff --git a/src/shared/format-table.c b/src/shared/format-table.c +index a13a198..2dc95e9 100644 +--- a/src/shared/format-table.c ++++ b/src/shared/format-table.c +@@ -2155,6 +2155,12 @@ int table_print(Table *t, FILE *f) { + if (!aligned) + return -ENOMEM; + ++ /* Drop trailing white spaces of last column when no cosmetics is set. */ ++ if (j == display_columns - 1 && ++ (!colors_enabled() || !table_data_color(d)) && ++ (!urlify_enabled() || !d->url)) ++ delete_trailing_chars(aligned, NULL); ++ + free_and_replace(buffer, aligned); + field = buffer; + } +diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c +index 24ee1df..b2943e6 100644 +--- a/src/test/test-format-table.c ++++ b/src/test/test-format-table.c +@@ -29,7 +29,7 @@ static void test_issue_9549(void) { + + printf("%s\n", formatted); + assert_se(streq(formatted, +- "NAME TYPE RO USAGE CREATED MODIFIED \n" ++ "NAME TYPE RO USAGE CREATED MODIFIED\n" + "foooo raw no 673.6M Wed 2018-07-11 00:10:33 J… Wed 2018-07-11 00:16:00 JST\n" + )); + } +@@ -72,7 +72,7 @@ static void test_multiline(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n")); ++ "lines \n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, (size_t) -1); +@@ -82,7 +82,7 @@ static void test_multiline(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n")); ++ "lines \n")); + formatted = mfree(formatted); + + assert_se(table_add_many(table, +@@ -123,7 +123,7 @@ static void test_multiline(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n" ++ "lines \n" + "short a\n" + " pair\n" + "short2 a\n" +@@ -138,7 +138,7 @@ static void test_multiline(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n" ++ "lines \n" + "short a\n" + " pair\n" + "short2 a\n" +@@ -186,7 +186,7 @@ static void test_strv(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n")); ++ "lines \n")); + formatted = mfree(formatted); + + table_set_cell_height_max(table, (size_t) -1); +@@ -196,7 +196,7 @@ static void test_strv(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n")); ++ "lines \n")); + formatted = mfree(formatted); + + assert_se(table_add_many(table, +@@ -237,7 +237,7 @@ static void test_strv(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n" ++ "lines \n" + "short a\n" + " pair\n" + "short2 a\n" +@@ -252,7 +252,7 @@ static void test_strv(void) { + "FOO BAR\n" + "three two\n" + "different lines\n" +- "lines \n" ++ "lines \n" + "short a\n" + " pair\n" + "short2 a\n" +@@ -333,7 +333,7 @@ static void test_strv_wrapped(void) { + assert_se(streq(formatted, + "FOO BAR\n" + "three different two lines\n" +- "lines \n" ++ "lines \n" + "short a pair\n" + "short2 a eight line ćęłł\n" + " ___5___ ___6___…\n")); +@@ -345,7 +345,7 @@ static void test_strv_wrapped(void) { + assert_se(streq(formatted, + "FOO BAR\n" + "three different two lines\n" +- "lines \n" ++ "lines \n" + "short a pair\n" + "short2 a eight line ćęłł\n" + " ___5___ ___6___\n" +@@ -358,7 +358,7 @@ static void test_strv_wrapped(void) { + assert_se(streq(formatted, + "FOO BAR\n" + "three different two lines\n" +- "lines \n" ++ "lines \n" + "short a pair\n" + "short2 a eight line ćęłł\n" + " ___5___ ___6___\n" +@@ -493,17 +493,17 @@ int main(int argc, char *argv[]) { + if (isatty(STDOUT_FILENO)) + assert_se(streq(formatted, + " no a long f… no a long f… a long fi…\n" +- " no fäää no fäää fäää \n" +- " yes fäää yes fäää fäää \n" +- " yes xxx yes xxx xxx \n" +- "5min 5min \n")); ++ " no fäää no fäää fäää\n" ++ " yes fäää yes fäää fäää\n" ++ " yes xxx yes xxx xxx\n" ++ "5min 5min \n")); + else + assert_se(streq(formatted, + " no a long field no a long field a long field\n" +- " no fäää no fäää fäää \n" +- " yes fäää yes fäää fäää \n" +- " yes xxx yes xxx xxx \n" +- "5min 5min \n")); ++ " no fäää no fäää fäää\n" ++ " yes fäää yes fäää fäää\n" ++ " yes xxx yes xxx xxx\n" ++ "5min 5min \n")); + + test_issue_9549(); + test_multiline(); diff --git a/debian/patches/time-util-fix-buffer-over-run.patch b/debian/patches/time-util-fix-buffer-over-run.patch new file mode 100644 index 0000000..65a6ed0 --- /dev/null +++ b/debian/patches/time-util-fix-buffer-over-run.patch @@ -0,0 +1,55 @@ +From: Yu Watanabe <watanabe.yu+github@gmail.com> +Date: Thu, 7 Jul 2022 18:27:02 +0900 +Subject: time-util: fix buffer-over-run + +Fixes #23928. + +(cherry picked from commit 9102c625a673a3246d7e73d8737f3494446bad4e) +(cherry picked from commit 72d4c15a946d20143cd4c6783c802124bc894dc7) +(cherry picked from commit c32530f5bdd11c74e8f5a86eecd7c36b3bae739f) +(cherry picked from commit b2a25b5e64345bd0bb7697a956d33afd6980286a) +(cherry picked from commit 858dc1ad609290cc4ca288acf87046ee295c3d51) +--- + src/basic/time-util.c | 2 +- + src/test/test-time-util.c | 8 ++++++++ + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/basic/time-util.c b/src/basic/time-util.c +index 5318d63..1909710 100644 +--- a/src/basic/time-util.c ++++ b/src/basic/time-util.c +@@ -574,7 +574,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { + t = b; + } + +- n = MIN((size_t) k, l); ++ n = MIN((size_t) k, l-1); + + l -= n; + p += n; +diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c +index cc391e8..877b24c 100644 +--- a/src/test/test-time-util.c ++++ b/src/test/test-time-util.c +@@ -243,6 +243,13 @@ static void test_format_timespan(usec_t accuracy) { + test_format_timespan_one(USEC_INFINITY, accuracy); + } + ++static void test_format_timespan2(void) { ++ /* See issue #23928. */ ++ _cleanup_free_ char *buf; ++ assert_se(buf = new(char, 5)); ++ assert_se(buf == format_timespan(buf, 5, 100005, 1000)); ++} ++ + static void test_timezone_is_valid(void) { + log_info("/* %s */", __func__); + +@@ -533,6 +540,7 @@ int main(int argc, char *argv[]) { + test_format_timespan(1); + test_format_timespan(USEC_PER_MSEC); + test_format_timespan(USEC_PER_SEC); ++ test_format_timespan2(); + test_timezone_is_valid(); + test_get_timezones(); + test_usec_add(); diff --git a/debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch b/debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch new file mode 100644 index 0000000..a8877b3 --- /dev/null +++ b/debian/patches/tmpfiles-st-may-have-been-used-uninitialized.patch @@ -0,0 +1,27 @@ +From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> +Date: Tue, 23 Nov 2021 15:05:58 +0100 +Subject: tmpfiles: 'st' may have been used uninitialized + +(cherry picked from commit 160dadc0350c77d612aa9d5569f57d9bc84c3dca) +(cherry picked from commit 7563de501246dccf5a9ea229933481aa1e7bd5c9) +(cherry picked from commit f54b97b1d05052bfee824ecc03ae9f07f6c37be8) +(cherry picked from commit ab927db9a7698ee1eceae14ecef7ab43ee3f104e) +--- + src/basic/rm-rf.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c +index cf671c2..a78aa4f 100644 +--- a/src/basic/rm-rf.c ++++ b/src/basic/rm-rf.c +@@ -128,7 +128,9 @@ static int rm_rf_children_inner( + assert(fd >= 0); + assert(fname); + +- if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) { ++ if (is_dir < 0 || ++ root_dev || ++ (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) { + + r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags); + if (r < 0) diff --git a/debian/patches/udev-always-create-device-symlinks-for-USB-disks.patch b/debian/patches/udev-always-create-device-symlinks-for-USB-disks.patch new file mode 100644 index 0000000..f373b68 --- /dev/null +++ b/debian/patches/udev-always-create-device-symlinks-for-USB-disks.patch @@ -0,0 +1,111 @@ +From: Yu Watanabe <watanabe.yu+github@gmail.com> +Date: Sun, 30 Oct 2022 09:43:05 +0900 +Subject: udev: always create device symlinks for USB disks + +Previously, ata_id might not be able to retrieve attributes correctly, +and properties from usb_id were used as a fallback. See issue #24921 +and PR #24923. To keep backward compatibility, still we need to create +symlinks based on USB serial. + +Fixes #25179. +--- + rules.d/60-persistent-storage.rules | 10 +++++-- + src/udev/udev-builtin-usb_id.c | 55 +++++++++++++++++++++++++++++-------- + 2 files changed, 51 insertions(+), 14 deletions(-) + +diff --git a/rules.d/60-persistent-storage.rules b/rules.d/60-persistent-storage.rules +index fc7f733..99e0c9a 100644 +--- a/rules.d/60-persistent-storage.rules ++++ b/rules.d/60-persistent-storage.rules +@@ -59,14 +59,20 @@ KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}== + # Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) + KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" + +-# Fall back usb_id for USB devices +-KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" ++# Also import properties from usb_id for USB devices ++KERNEL=="sd*[!0-9]|sr*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" + + # SCSI devices + KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" + KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" + KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" + KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" ++# Previously, ata_id in the above might not be able to retrieve attributes correctly, ++# and properties from usb_id were used as a fallback. See issue #24921 and PR #24923. ++# To keep backward compatibility, still we need to create symlinks based on USB serial. ++# See issue #25179. ++KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_USB_SERIAL}=="?*", SYMLINK+="disk/by-id/usb-$env{ID_USB_SERIAL}" ++KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_USB_SERIAL}=="?*", SYMLINK+="disk/by-id/usb-$env{ID_USB_SERIAL}-part%n" + + # PMEM devices + KERNEL=="pmem*", ENV{DEVTYPE}=="disk", ATTRS{uuid}=="?*", SYMLINK+="disk/by-id/pmem-$attr{uuid}" +diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c +index fa554e7..3910ffa 100644 +--- a/src/udev/udev-builtin-usb_id.c ++++ b/src/udev/udev-builtin-usb_id.c +@@ -430,21 +430,52 @@ fallback: + if (!isempty(instance_str)) + strpcpyl(&s, l, "-", instance_str, NULL); + +- udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); +- udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); +- udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); +- udev_builtin_add_property(dev, test, "ID_MODEL", model_str); +- udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); +- udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); +- udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); +- udev_builtin_add_property(dev, test, "ID_SERIAL", serial); ++ if (sd_device_get_property_value(dev, "ID_BUS", NULL) >= 0) ++ log_device_debug(dev, "ID_BUS property is already set, setting only properties prefixed with \"ID_USB_\"."); ++ else { ++ udev_builtin_add_property(dev, test, "ID_BUS", "usb"); ++ ++ udev_builtin_add_property(dev, test, "ID_MODEL", model_str); ++ udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); ++ udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); ++ ++ udev_builtin_add_property(dev, test, "ID_SERIAL", serial); ++ if (!isempty(serial_str)) ++ udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); ++ ++ udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); ++ udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); ++ udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); ++ ++ udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); ++ ++ if (!isempty(type_str)) ++ udev_builtin_add_property(dev, test, "ID_TYPE", type_str); ++ ++ if (!isempty(instance_str)) ++ udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); ++ } ++ ++ /* Also export the same values in the above by prefixing ID_USB_. */ ++ udev_builtin_add_property(dev, test, "ID_USB_MODEL", model_str); ++ udev_builtin_add_property(dev, test, "ID_USB_MODEL_ENC", model_str_enc); ++ udev_builtin_add_property(dev, test, "ID_USB_MODEL_ID", product_id); ++ udev_builtin_add_property(dev, test, "ID_USB_SERIAL", serial); + if (!isempty(serial_str)) +- udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); ++ udev_builtin_add_property(dev, test, "ID_USB_SERIAL_SHORT", serial_str); ++ ++ udev_builtin_add_property(dev, test, "ID_USB_VENDOR", vendor_str); ++ udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ENC", vendor_str_enc); ++ udev_builtin_add_property(dev, test, "ID_USB_VENDOR_ID", vendor_id); ++ ++ udev_builtin_add_property(dev, test, "ID_USB_REVISION", revision_str); ++ + if (!isempty(type_str)) +- udev_builtin_add_property(dev, test, "ID_TYPE", type_str); ++ udev_builtin_add_property(dev, test, "ID_USB_TYPE", type_str); ++ + if (!isempty(instance_str)) +- udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); +- udev_builtin_add_property(dev, test, "ID_BUS", "usb"); ++ udev_builtin_add_property(dev, test, "ID_USB_INSTANCE", instance_str); ++ + if (!isempty(packed_if_str)) + udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); + if (ifnum) diff --git a/debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch b/debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch new file mode 100644 index 0000000..0aee515 --- /dev/null +++ b/debian/patches/udev-first-set-properties-based-on-usb-subsystem.patch @@ -0,0 +1,34 @@ +From: Yu Watanabe <watanabe.yu+github@gmail.com> +Date: Thu, 3 Nov 2022 09:39:36 +0900 +Subject: udev: first set properties based on usb subsystem + +After 479da1107a0d4e2f7ef5cd938512b87a0e45f180, the usb_id builtin +command does not set ID_SERIAL if ID_BUS is already set. +Before the commit, all properties set based on pci bus were overwritten +by the usb_id, hence now it is sufficient setting them only when ID_BUS is +not set yet. + +Fixes #25238. + +(cherry picked from commit 01e704eba982fbc1517287cd261d229ff8e0a779) +--- + rules.d/60-serial.rules | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/rules.d/60-serial.rules b/rules.d/60-serial.rules +index f303e27..c133f26 100644 +--- a/rules.d/60-serial.rules ++++ b/rules.d/60-serial.rules +@@ -3,9 +3,10 @@ + ACTION=="remove", GOTO="serial_end" + SUBSYSTEM!="tty", GOTO="serial_end" + +-SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" +-SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci" + SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" ++SUBSYSTEMS=="pci", ENV{ID_BUS}=="", ENV{ID_BUS}="pci", \ ++ ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}", \ ++ IMPORT{builtin}="hwdb --subsystem=pci" + + # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices + KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" diff --git a/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch b/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch new file mode 100644 index 0000000..cabec71 --- /dev/null +++ b/debian/patches/udevadm-trigger-do-not-return-immediately-on-EACCES.patch @@ -0,0 +1,58 @@ +From: Yu Watanabe <watanabe.yu+github@gmail.com> +Date: Sat, 20 Feb 2021 16:30:23 +0900 +Subject: udevadm-trigger: do not return immediately on EACCES + +Prompted by https://github.com/systemd/systemd/pull/18559. + +(cherry picked from commit 0e789e6d48046d43c50dd949a71ac56f1127bb96) +--- + src/udev/udevadm-trigger.c | 32 +++++++++++++++++++++++++++++--- + 1 file changed, 29 insertions(+), 3 deletions(-) + +diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c +index 5c74184..da9b89a 100644 +--- a/src/udev/udevadm-trigger.c ++++ b/src/udev/udevadm-trigger.c +@@ -45,13 +45,39 @@ static int exec_list(sd_device_enumerator *e, const char *action, Set **settle_s + + r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) { ++ /* ENOENT may be returned when a device does not have /uevent or is already ++ * removed. Hence, this is logged at debug level and ignored. ++ * ++ * ENODEV may be returned by some buggy device drivers e.g. /sys/devices/vio. ++ * See, ++ * https://github.com/systemd/systemd/issues/13652#issuecomment-535129791 and ++ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1845319. ++ * So, this error is ignored, but logged at warning level to encourage people to ++ * fix the driver. ++ * ++ * EROFS is returned when /sys is read only. In that case, all subsequent ++ * writes will also fail, hence return immediately. ++ * ++ * EACCES or EPERM may be returned when this is invoked by non-priviledged user. ++ * We do NOT return immediately, but continue operation and propagate the error. ++ * Why? Some device can be owned by a user, e.g., network devices configured in ++ * a network namespace. See, https://github.com/systemd/systemd/pull/18559 and ++ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebb4a4bf76f164457184a3f43ebc1552416bc823 ++ * ++ * All other errors are logged at error level, but let's continue the operation, ++ * and propagate the error. ++ */ ++ + bool ignore = IN_SET(r, -ENOENT, -ENODEV); ++ int level = ++ r == -ENOENT ? LOG_DEBUG : ++ r == -ENODEV ? LOG_WARNING : LOG_ERR; + +- log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r, ++ log_full_errno(level, r, + "Failed to write '%s' to '%s'%s: %m", + action, filename, ignore ? ", ignoring" : ""); +- if (IN_SET(r, -EACCES, -EROFS)) +- /* Inovoked by unpriviledged user, or read only filesystem. Return earlier. */ ++ ++ if (r == -EROFS) + return r; + if (ret == 0 && !ignore) + ret = r; diff --git a/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch b/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch new file mode 100644 index 0000000..c28266b --- /dev/null +++ b/debian/patches/unit-name-generate-a-clear-error-code-when-convertin.patch @@ -0,0 +1,59 @@ +From: Lennart Poettering <lennart@poettering.net> +Date: Tue, 1 Jun 2021 19:43:55 +0200 +Subject: unit-name: generate a clear error code when converting an overly + long fs path to a unit name + +(cherry picked from commit 9d5acfab20c5f1177d877d0bec18063c0a6c5929) +(cherry picked from commit 1579dce2c2a162bb09afb9a8a46fd4f7e8fbf1d5) +(cherry picked from commit 0488b743e9c6ab1e885933eebda4ba9232003a2a) +--- + src/basic/unit-name.c | 6 ++++++ + src/test/test-unit-name.c | 4 ++-- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c +index c1529bb..5f595af 100644 +--- a/src/basic/unit-name.c ++++ b/src/basic/unit-name.c +@@ -528,6 +528,9 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) { + if (!s) + return -ENOMEM; + ++ if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */ ++ return -ENAMETOOLONG; ++ + /* Refuse this if this got too long or for some other reason didn't result in a valid name */ + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return -EINVAL; +@@ -559,6 +562,9 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha + if (!s) + return -ENOMEM; + ++ if (strlen(s) >= UNIT_NAME_MAX) /* Return a slightly more descriptive error for this specific condition */ ++ return -ENAMETOOLONG; ++ + /* Refuse this if this got too long or for some other reason didn't result in a valid name */ + if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE)) + return -EINVAL; +diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c +index ece78aa..c0b7971 100644 +--- a/src/test/test-unit-name.c ++++ b/src/test/test-unit-name.c +@@ -130,7 +130,7 @@ static void test_unit_name_from_path(void) { + test_unit_name_from_path_one("///", ".mount", "-.mount", 0); + test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL); + test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL); +- test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -EINVAL); ++ test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -ENAMETOOLONG); + } + + static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) { +@@ -160,7 +160,7 @@ static void test_unit_name_from_path_instance(void) { + test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL); + test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL); + test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0); +- test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -EINVAL); ++ test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -ENAMETOOLONG); + } + + static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) { diff --git a/debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch b/debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch new file mode 100644 index 0000000..9757987 --- /dev/null +++ b/debian/patches/virt-Fix-the-detection-for-Hyper-V-VMs.patch @@ -0,0 +1,38 @@ +From: Boqun Feng <boqun.feng@gmail.com> +Date: Tue, 23 Nov 2021 15:09:26 +0800 +Subject: virt: Fix the detection for Hyper-V VMs + +Use product_version instead of product_name in DMI table and the string +"Hyper-V" to avoid misdetection. + +Fixes: #21468 + +Signed-off-by: Boqun Feng <boqun.feng@gmail.com> +(cherry picked from commit 76eec0649936d9ae2f9087769f463feaf0cf5cb4) +--- + src/basic/virt.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index 0d45ee6..54befd9 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -140,7 +140,8 @@ static int detect_vm_dmi(void) { + "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */ + "/sys/class/dmi/id/sys_vendor", + "/sys/class/dmi/id/board_vendor", +- "/sys/class/dmi/id/bios_vendor" ++ "/sys/class/dmi/id/bios_vendor", ++ "/sys/class/dmi/id/product_version" /* For Hyper-V VMs test */ + }; + + static const struct { +@@ -158,7 +159,7 @@ static int detect_vm_dmi(void) { + { "Parallels", VIRTUALIZATION_PARALLELS }, + /* https://wiki.freebsd.org/bhyve */ + { "BHYVE", VIRTUALIZATION_BHYVE }, +- { "Microsoft", VIRTUALIZATION_MICROSOFT }, ++ { "Hyper-V", VIRTUALIZATION_MICROSOFT }, + }; + unsigned i; + int r; diff --git a/debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch b/debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch new file mode 100644 index 0000000..373a77f --- /dev/null +++ b/debian/patches/virt-Support-detection-for-ARM64-Hyper-V-guests.patch @@ -0,0 +1,28 @@ +From: Boqun Feng <boqun.feng@gmail.com> +Date: Wed, 13 Oct 2021 11:32:09 +0800 +Subject: virt: Support detection for ARM64 Hyper-V guests + +The detection of Microsoft Hyper-V VMs is done by cpuid currently, +however there is no cpuid on ARM64. And since ARM64 is now a supported +architecture for Microsoft Hyper-V guests[1], then use DMI tables to +detect a Hyper-V guest, which is more generic and works for ARM64. + +[1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7aff79e297ee1aa0126924921fd87a4ae59d2467 + +(cherry picked from commit 506bbc8569014253ea8614b680ccbc4fc2513a87) +--- + src/basic/virt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index 7d78a40..0d45ee6 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -158,6 +158,7 @@ static int detect_vm_dmi(void) { + { "Parallels", VIRTUALIZATION_PARALLELS }, + /* https://wiki.freebsd.org/bhyve */ + { "BHYVE", VIRTUALIZATION_BHYVE }, ++ { "Microsoft", VIRTUALIZATION_MICROSOFT }, + }; + unsigned i; + int r; diff --git a/debian/patches/virt-detect-OpenStack-Nova-instance.patch b/debian/patches/virt-detect-OpenStack-Nova-instance.patch new file mode 100644 index 0000000..87686b2 --- /dev/null +++ b/debian/patches/virt-detect-OpenStack-Nova-instance.patch @@ -0,0 +1,21 @@ +From: Michael Biebl <biebl@debian.org> +Date: Sun, 7 Aug 2022 15:21:12 +0200 +Subject: virt: detect OpenStack Nova instance + +(cherry picked from commit 01d9fbccddd694bc584aed24eaa0543f831dc929) +--- + src/basic/virt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index 54befd9..78ee1b8 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -149,6 +149,7 @@ static int detect_vm_dmi(void) { + int id; + } dmi_vendor_table[] = { + { "KVM", VIRTUALIZATION_KVM }, ++ { "OpenStack", VIRTUALIZATION_KVM }, /* Detect OpenStack instance as KVM in non x86 architecture */ + { "QEMU", VIRTUALIZATION_QEMU }, + { "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */ + { "VMW", VIRTUALIZATION_VMWARE }, |