summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2021-07-29 12:46:06 +0200
committerThomas Haller <thaller@redhat.com>2021-07-29 18:55:39 +0200
commit08ee0a83a1d5b7e6c1c2f1a907a8c3bae4dd45f9 (patch)
tree965c3a99dbb967703c09038b817a409de899fa9b
parent02a4e00d562e8e73d7ede7644009e4327617b1c2 (diff)
systemd: update code from upstream (2021-07-29)
This is a direct dump from systemd git. ====== SYSTEMD_DIR=../systemd COMMIT=7925d693a79cf1e4a3d1539402b3160bb9a3ab12 ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) git ls-files -z :/src/libnm-systemd-core/src/ \ :/src/libnm-systemd-shared/src/ \ :/src/libnm-std-aux/unaligned.h | \ xargs -0 rm -f nm_copy_sd_shared() { mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1" } nm_copy_sd_core() { mkdir -p "./src/libnm-systemd-core/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1" } nm_copy_sd_stdaux() { mkdir -p "./src/libnm-std-aux/" cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}" } nm_copy_sd_core "src/libsystemd-network/arp-util.c" nm_copy_sd_core "src/libsystemd-network/arp-util.h" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h" nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c" nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h" nm_copy_sd_core "src/libsystemd-network/lldp-internal.h" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h" nm_copy_sd_core "src/libsystemd-network/lldp-network.c" nm_copy_sd_core "src/libsystemd-network/lldp-network.h" nm_copy_sd_core "src/libsystemd-network/network-common.c" nm_copy_sd_core "src/libsystemd-network/network-common.h" nm_copy_sd_core "src/libsystemd-network/network-internal.c" nm_copy_sd_core "src/libsystemd-network/network-internal.h" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4acd.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c" nm_copy_sd_core "src/libsystemd-network/sd-lldp.c" nm_copy_sd_core "src/libsystemd/sd-event/event-source.h" nm_copy_sd_core "src/libsystemd/sd-event/event-util.c" nm_copy_sd_core "src/libsystemd/sd-event/event-util.h" nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h" nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c" nm_copy_sd_core "src/systemd/_sd-common.h" nm_copy_sd_core "src/systemd/sd-dhcp-client.h" nm_copy_sd_core "src/systemd/sd-dhcp-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp-option.h" nm_copy_sd_core "src/systemd/sd-dhcp6-client.h" nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp6-option.h" nm_copy_sd_core "src/systemd/sd-event.h" nm_copy_sd_core "src/systemd/sd-id128.h" nm_copy_sd_core "src/systemd/sd-ipv4acd.h" nm_copy_sd_core "src/systemd/sd-ipv4ll.h" nm_copy_sd_core "src/systemd/sd-lldp.h" nm_copy_sd_core "src/systemd/sd-ndisc.h" nm_copy_sd_shared "src/basic/alloc-util.c" nm_copy_sd_shared "src/basic/alloc-util.h" nm_copy_sd_shared "src/basic/async.h" nm_copy_sd_shared "src/basic/cgroup-util.h" nm_copy_sd_shared "src/basic/dns-def.h" nm_copy_sd_shared "src/basic/env-file.c" nm_copy_sd_shared "src/basic/env-file.h" nm_copy_sd_shared "src/basic/env-util.c" nm_copy_sd_shared "src/basic/env-util.h" nm_copy_sd_shared "src/basic/errno-util.h" nm_copy_sd_shared "src/basic/escape.c" nm_copy_sd_shared "src/basic/escape.h" nm_copy_sd_shared "src/basic/ether-addr-util.c" nm_copy_sd_shared "src/basic/ether-addr-util.h" nm_copy_sd_shared "src/basic/extract-word.c" nm_copy_sd_shared "src/basic/extract-word.h" nm_copy_sd_shared "src/basic/fd-util.c" nm_copy_sd_shared "src/basic/fd-util.h" nm_copy_sd_shared "src/basic/fileio.c" nm_copy_sd_shared "src/basic/fileio.h" nm_copy_sd_shared "src/basic/format-util.c" nm_copy_sd_shared "src/basic/format-util.h" nm_copy_sd_shared "src/basic/fs-util.c" nm_copy_sd_shared "src/basic/fs-util.h" nm_copy_sd_shared "src/basic/hash-funcs.c" nm_copy_sd_shared "src/basic/hash-funcs.h" nm_copy_sd_shared "src/basic/hashmap.c" nm_copy_sd_shared "src/basic/hashmap.h" nm_copy_sd_shared "src/basic/hexdecoct.c" nm_copy_sd_shared "src/basic/hexdecoct.h" nm_copy_sd_shared "src/basic/hostname-util.c" nm_copy_sd_shared "src/basic/hostname-util.h" nm_copy_sd_shared "src/basic/in-addr-util.c" nm_copy_sd_shared "src/basic/in-addr-util.h" nm_copy_sd_shared "src/basic/io-util.c" nm_copy_sd_shared "src/basic/io-util.h" nm_copy_sd_shared "src/basic/list.h" nm_copy_sd_shared "src/basic/log.h" nm_copy_sd_shared "src/basic/macro.h" nm_copy_sd_shared "src/basic/memory-util.c" nm_copy_sd_shared "src/basic/memory-util.h" nm_copy_sd_shared "src/basic/mempool.c" nm_copy_sd_shared "src/basic/mempool.h" nm_copy_sd_shared "src/basic/missing_fcntl.h" nm_copy_sd_shared "src/basic/missing_random.h" nm_copy_sd_shared "src/basic/missing_socket.h" nm_copy_sd_shared "src/basic/missing_stat.h" nm_copy_sd_shared "src/basic/missing_syscall.h" nm_copy_sd_shared "src/basic/missing_type.h" nm_copy_sd_shared "src/basic/ordered-set.c" nm_copy_sd_shared "src/basic/ordered-set.h" nm_copy_sd_shared "src/basic/parse-util.c" nm_copy_sd_shared "src/basic/parse-util.h" nm_copy_sd_shared "src/basic/path-util.c" nm_copy_sd_shared "src/basic/path-util.h" nm_copy_sd_shared "src/basic/prioq.c" nm_copy_sd_shared "src/basic/prioq.h" nm_copy_sd_shared "src/basic/process-util.c" nm_copy_sd_shared "src/basic/process-util.h" nm_copy_sd_shared "src/basic/random-util.c" nm_copy_sd_shared "src/basic/random-util.h" nm_copy_sd_shared "src/basic/ratelimit.c" nm_copy_sd_shared "src/basic/ratelimit.h" nm_copy_sd_shared "src/basic/set.h" nm_copy_sd_shared "src/basic/signal-util.c" nm_copy_sd_shared "src/basic/signal-util.h" nm_copy_sd_shared "src/basic/siphash24.h" nm_copy_sd_shared "src/basic/socket-util.c" nm_copy_sd_shared "src/basic/socket-util.h" nm_copy_sd_shared "src/basic/sort-util.h" nm_copy_sd_shared "src/basic/sparse-endian.h" nm_copy_sd_shared "src/basic/stat-util.c" nm_copy_sd_shared "src/basic/stat-util.h" nm_copy_sd_shared "src/basic/stdio-util.h" nm_copy_sd_shared "src/basic/string-table.c" nm_copy_sd_shared "src/basic/string-table.h" nm_copy_sd_shared "src/basic/string-util.c" nm_copy_sd_shared "src/basic/string-util.h" nm_copy_sd_shared "src/basic/strv.c" nm_copy_sd_shared "src/basic/strv.h" nm_copy_sd_shared "src/basic/strxcpyx.c" nm_copy_sd_shared "src/basic/strxcpyx.h" nm_copy_sd_shared "src/basic/time-util.c" nm_copy_sd_shared "src/basic/time-util.h" nm_copy_sd_shared "src/basic/tmpfile-util.c" nm_copy_sd_shared "src/basic/tmpfile-util.h" nm_copy_sd_shared "src/basic/umask-util.h" nm_copy_sd_shared "src/basic/user-util.h" nm_copy_sd_shared "src/basic/utf8.c" nm_copy_sd_shared "src/basic/utf8.h" nm_copy_sd_shared "src/basic/util.c" nm_copy_sd_shared "src/basic/util.h" nm_copy_sd_shared "src/fundamental/macro-fundamental.h" nm_copy_sd_shared "src/fundamental/string-util-fundamental.c" nm_copy_sd_shared "src/fundamental/string-util-fundamental.h" nm_copy_sd_shared "src/fundamental/type.h" nm_copy_sd_shared "src/shared/dns-domain.c" nm_copy_sd_shared "src/shared/dns-domain.h" nm_copy_sd_shared "src/shared/log-link.h" nm_copy_sd_shared "src/shared/web-util.c" nm_copy_sd_shared "src/shared/web-util.h" nm_copy_sd_stdaux "src/basic/unaligned.h"
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/arp-util.c92
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/arp-util.h28
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c153
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c10
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c159
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c34
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c14
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c29
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ipv4acd.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ipv4ll.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c51
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c10
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.c56
-rw-r--r--src/libnm-systemd-shared/src/basic/extract-word.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c501
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c53
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/format-util.h17
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c183
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c5
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.c48
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c3
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c10
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c136
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c35
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c231
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h46
39 files changed, 970 insertions, 1042 deletions
diff --git a/src/libnm-systemd-core/src/libsystemd-network/arp-util.c b/src/libnm-systemd-core/src/libsystemd-network/arp-util.c
index 327fb2ff3e..d8d94ab5cb 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/arp-util.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/arp-util.c
@@ -8,11 +8,13 @@
#include <netinet/if_ether.h>
#include "arp-util.h"
+#include "ether-addr-util.h"
#include "fd-util.h"
+#include "in-addr-util.h"
#include "unaligned.h"
#include "util.h"
-int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
+int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
@@ -46,13 +48,13 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
- BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
- BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */
+ BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
@@ -61,15 +63,25 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
};
struct sock_fprog fprog = {
- .len = ELEMENTSOF(filter),
- .filter = (struct sock_filter*) filter
+ .len = ELEMENTSOF(filter),
+ .filter = (struct sock_filter*) filter,
};
+
+ assert(fd >= 0);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) {
union sockaddr_union link = {
- .ll.sll_family = AF_PACKET,
+ .ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
- .ll.sll_ifindex = ifindex,
- .ll.sll_halen = ETH_ALEN,
- .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
_cleanup_close_ int s = -1;
int r;
@@ -80,59 +92,57 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
if (s < 0)
return -errno;
- r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+ r = arp_update_filter(s, a, eth_mac);
if (r < 0)
- return -errno;
+ return r;
- r = bind(s, &link.sa, sizeof(link.ll));
- if (r < 0)
+ if (bind(s, &link.sa, sizeof(link.ll)) < 0)
return -errno;
return TAKE_FD(s);
}
-static int arp_send_packet(int fd, int ifindex,
- be32_t pa, const struct ether_addr *ha,
- bool announce) {
+int arp_send_packet(
+ int fd,
+ int ifindex,
+ const struct in_addr *pa,
+ const struct ether_addr *ha,
+ bool announce) {
+
union sockaddr_union link = {
- .ll.sll_family = AF_PACKET,
+ .ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
- .ll.sll_ifindex = ifindex,
- .ll.sll_halen = ETH_ALEN,
- .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ .ll.sll_ifindex = ifindex,
+ .ll.sll_halen = ETH_ALEN,
+ .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
struct ether_arp arp = {
- .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
- .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
- .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
- .ea_hdr.ar_pln = sizeof(be32_t), /* PLEN */
- .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
+ .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */
+ .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */
+ .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */
+ .ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */
+ .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */
};
- int r;
+ ssize_t n;
assert(fd >= 0);
- assert(pa != 0);
+ assert(ifindex > 0);
+ assert(pa);
+ assert(in4_addr_is_set(pa));
assert(ha);
+ assert(!ether_addr_is_null(ha));
memcpy(&arp.arp_sha, ha, ETH_ALEN);
- memcpy(&arp.arp_tpa, &pa, sizeof(pa));
+ memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr));
if (announce)
- memcpy(&arp.arp_spa, &pa, sizeof(pa));
+ memcpy(&arp.arp_spa, pa, sizeof(struct in_addr));
- r = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
- if (r < 0)
+ n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll));
+ if (n < 0)
return -errno;
+ if (n != sizeof(struct ether_arp))
+ return -EIO;
return 0;
}
-
-int arp_send_probe(int fd, int ifindex,
- be32_t pa, const struct ether_addr *ha) {
- return arp_send_packet(fd, ifindex, pa, ha, false);
-}
-
-int arp_send_announcement(int fd, int ifindex,
- be32_t pa, const struct ether_addr *ha) {
- return arp_send_packet(fd, ifindex, pa, ha, true);
-}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/arp-util.h b/src/libnm-systemd-core/src/libsystemd-network/arp-util.h
index 2dac8cfbaa..e8615a158d 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/arp-util.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/arp-util.h
@@ -6,13 +6,31 @@
***/
#include <net/ethernet.h>
+#include <netinet/in.h>
#include "socket-util.h"
#include "sparse-endian.h"
-int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac);
+int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac);
+int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac);
-int arp_send_probe(int fd, int ifindex,
- be32_t pa, const struct ether_addr *ha);
-int arp_send_announcement(int fd, int ifindex,
- be32_t pa, const struct ether_addr *ha);
+int arp_send_packet(
+ int fd,
+ int ifindex,
+ const struct in_addr *pa,
+ const struct ether_addr *ha,
+ bool announce);
+static inline int arp_send_probe(
+ int fd,
+ int ifindex,
+ const struct in_addr *pa,
+ const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, false);
+}
+static inline int arp_send_announcement(
+ int fd,
+ int ifindex,
+ const struct in_addr *pa,
+ const struct ether_addr *ha) {
+ return arp_send_packet(fd, ifindex, pa, ha, true);
+}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
index ff021f4eae..7fe660466f 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
@@ -238,7 +238,7 @@ int sd_dhcp_client_set_callback(
int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
assert_return(client, -EINVAL);
- client->request_broadcast = !!broadcast;
+ client->request_broadcast = broadcast;
return 0;
}
@@ -1587,9 +1587,17 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
if (r != DHCP_FORCERENEW)
return -ENOMSG;
+#if 0
log_dhcp_client(client, "FORCERENEW");
return 0;
+#else
+ /* FIXME: Ignore FORCERENEW requests until we implement RFC3118 (Authentication for DHCP
+ * Messages) and/or RFC6704 (Forcerenew Nonce Authentication), as unauthenticated FORCERENEW
+ * requests causes a security issue (TALOS-2020-1142, CVE-2020-13529). */
+ log_dhcp_client(client, "Received FORCERENEW, ignoring.");
+ return -ENOMSG;
+#endif
}
static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
@@ -1678,7 +1686,6 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
static int client_set_lease_timeouts(sd_dhcp_client *client) {
usec_t time_now;
- char time_string[FORMAT_TIMESPAN_MAX];
int r;
assert(client);
@@ -1740,7 +1747,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return 0;
log_dhcp_client(client, "lease expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->expire_time - time_now, USEC_PER_SEC));
+ FORMAT_TIMESPAN(client->expire_time - time_now, USEC_PER_SEC));
/* arm T2 timeout */
r = event_reset_time(client->event, &client->timeout_t2,
@@ -1756,7 +1763,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
return 0;
log_dhcp_client(client, "T2 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t2_time - time_now, USEC_PER_SEC));
+ FORMAT_TIMESPAN(client->t2_time - time_now, USEC_PER_SEC));
/* arm T1 timeout */
r = event_reset_time(client->event, &client->timeout_t1,
@@ -1769,15 +1776,14 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
if (client->t1_time > time_now)
log_dhcp_client(client, "T1 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->t1_time - time_now, USEC_PER_SEC));
+ FORMAT_TIMESPAN(client->t1_time - time_now, USEC_PER_SEC));
return 0;
}
static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
DHCP_CLIENT_DONT_DESTROY(client);
- char time_string[FORMAT_TIMESPAN_MAX];
- int r = 0, notify_event = 0;
+ int r, notify_event;
assert(client);
assert(client->event);
@@ -1787,22 +1793,19 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
case DHCP_STATE_SELECTING:
r = client_handle_offer(client, message, len);
- if (r >= 0) {
-
- client->state = DHCP_STATE_REQUESTING;
- client->attempt = 0;
+ if (r == -ENOMSG)
+ return 0; /* invalid message, let's ignore it */
+ if (r < 0)
+ goto error;
- r = event_reset_time(client->event, &client->timeout_resend,
- clock_boottime_or_monotonic(),
- 0, 0,
- client_timeout_resend, client,
- client->event_priority, "dhcp4-resend-timer", true);
- if (r < 0)
- goto error;
- } else if (r == -ENOMSG)
- /* invalid message, let's ignore it */
- return 0;
+ client->state = DHCP_STATE_REQUESTING;
+ client->attempt = 0;
+ r = event_reset_time(client->event, &client->timeout_resend,
+ clock_boottime_or_monotonic(),
+ 0, 0,
+ client_timeout_resend, client,
+ client->event_priority, "dhcp4-resend-timer", true);
break;
case DHCP_STATE_REBOOTING:
@@ -1811,47 +1814,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
case DHCP_STATE_REBINDING:
r = client_handle_ack(client, message, len);
- if (r >= 0) {
- client->start_delay = 0;
- (void) event_source_disable(client->timeout_resend);
- client->receive_message =
- sd_event_source_unref(client->receive_message);
- client->fd = safe_close(client->fd);
-
- if (IN_SET(client->state, DHCP_STATE_REQUESTING,
- DHCP_STATE_REBOOTING))
- notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
- else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
- notify_event = r;
-
- client->state = DHCP_STATE_BOUND;
- client->attempt = 0;
-
- client->last_addr = client->lease->address;
-
- r = client_set_lease_timeouts(client);
- if (r < 0) {
- log_dhcp_client(client, "could not set lease timeouts");
- goto error;
- }
-
- r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
- if (r < 0) {
- log_dhcp_client(client, "could not bind UDP socket");
- goto error;
- }
-
- client->fd = r;
-
- client_initialize_io_events(client, client_receive_message_udp);
-
- if (notify_event) {
- client_notify(client, notify_event);
- if (client->state == DHCP_STATE_STOPPED)
- return 0;
- }
-
- } else if (r == -EADDRNOTAVAIL) {
+ if (r == -ENOMSG)
+ return 0; /* invalid message, let's ignore it */
+ if (r == -EADDRNOTAVAIL) {
/* got a NAK, let's restart the client */
client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
@@ -1863,39 +1828,75 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
if (r < 0)
goto error;
- log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX,
- client->start_delay, USEC_PER_SEC));
+ log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC));
client->start_delay = CLAMP(client->start_delay * 2,
RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
-
- return 0;
- } else if (r == -ENOMSG)
- /* invalid message, let's ignore it */
return 0;
+ }
+ if (r < 0)
+ goto error;
+ if (IN_SET(client->state, DHCP_STATE_REQUESTING, DHCP_STATE_REBOOTING))
+ notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
+ else
+ notify_event = r;
+
+ client->start_delay = 0;
+ (void) event_source_disable(client->timeout_resend);
+ client->receive_message = sd_event_source_unref(client->receive_message);
+ client->fd = safe_close(client->fd);
+
+ client->state = DHCP_STATE_BOUND;
+ client->attempt = 0;
+
+ client->last_addr = client->lease->address;
+
+ r = client_set_lease_timeouts(client);
+ if (r < 0) {
+ log_dhcp_client(client, "could not set lease timeouts");
+ goto error;
+ }
+
+ r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
+ if (r < 0) {
+ log_dhcp_client(client, "could not bind UDP socket");
+ goto error;
+ }
+
+ client->fd = r;
+
+ client_initialize_io_events(client, client_receive_message_udp);
+
+ if (IN_SET(client->state, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING) &&
+ notify_event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
+ /* FIXME: hmm, maybe this is a bug... */
+ log_dhcp_client(client, "client_handle_ack() returned SD_DHCP_CLIENT_EVENT_IP_ACQUIRE while DHCP client is %s the address, skipping callback.",
+ client->state == DHCP_STATE_RENEWING ? "renewing" : "rebinding");
+ else
+ client_notify(client, notify_event);
break;
case DHCP_STATE_BOUND:
r = client_handle_forcerenew(client, message, len);
- if (r >= 0) {
- r = client_timeout_t1(NULL, 0, client);
- if (r < 0)
- goto error;
- } else if (r == -ENOMSG)
- /* invalid message, let's ignore it */
- return 0;
+ if (r == -ENOMSG)
+ return 0; /* invalid message, let's ignore it */
+ if (r < 0)
+ goto error;
+ r = client_timeout_t1(NULL, 0, client);
break;
case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT:
-
+ r = 0;
break;
case DHCP_STATE_STOPPED:
r = -EINVAL;
goto error;
+ default:
+ assert_not_reached("invalid state");
}
error:
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
index e8c47f429a..079f154acd 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
@@ -946,7 +946,6 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
usec_t max_retransmit_duration = 0;
uint8_t max_retransmit_count = 0;
- char time_string[FORMAT_TIMESPAN_MAX];
assert(s);
assert(client);
@@ -1042,7 +1041,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
}
log_dhcp6_client(client, "Next retransmission in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
+ FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC));
r = event_reset_time(client->event, &client->timeout_resend,
clock_boottime_or_monotonic(),
@@ -1566,7 +1565,6 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
int r;
usec_t timeout, time_now;
- char time_string[FORMAT_TIMESPAN_MAX];
uint32_t lifetime_t1, lifetime_t2;
assert_return(client, -EINVAL);
@@ -1639,8 +1637,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
- log_dhcp6_client(client, "T1 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
+ log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
r = event_reset_time(client->event, &client->timeout_t1,
clock_boottime_or_monotonic(),
@@ -1652,8 +1649,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
- log_dhcp6_client(client, "T2 expires in %s",
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
+ log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
r = event_reset_time(client->event, &client->timeout_t2,
clock_boottime_or_monotonic(),
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c
index b4af60741a..c6aa9aa11a 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c
@@ -18,6 +18,7 @@
#include "fd-util.h"
#include "in-addr-util.h"
#include "log-link.h"
+#include "memory-util.h"
#include "network-common.h"
#include "random-util.h"
#include "siphash24.h"
@@ -64,7 +65,7 @@ struct sd_ipv4acd {
sd_event_source *timer_event_source;
usec_t defend_window;
- be32_t address;
+ struct in_addr address;
/* External */
struct ether_addr mac_addr;
@@ -72,7 +73,9 @@ struct sd_ipv4acd {
sd_event *event;
int event_priority;
sd_ipv4acd_callback_t callback;
- void* userdata;
+ void *userdata;
+ sd_ipv4acd_check_mac_callback_t check_mac_callback;
+ void *check_mac_userdata;
};
#define log_ipv4acd_errno(acd, error, fmt, ...) \
@@ -208,18 +211,6 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u
acd->event_priority, "ipv4acd-timer", true);
}
-static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, struct ether_arp *arp) {
- assert(acd);
- assert(arp);
-
- /* see the BPF */
- if (memcmp(arp->arp_spa, &acd->address, sizeof(acd->address)) == 0)
- return true;
-
- /* the TPA matched instead of the SPA, this is not a conflict */
- return false;
-}
-
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ipv4acd *acd = userdata;
int r = 0;
@@ -229,38 +220,32 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
switch (acd->state) {
case IPV4ACD_STATE_STARTED:
+ acd->defend_window = 0;
+
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
if (acd->n_conflict >= MAX_CONFLICTS) {
- char ts[FORMAT_TIMESPAN_MAX];
- log_ipv4acd(acd, "Max conflicts reached, delaying by %s", format_timespan(ts, sizeof(ts), RATE_LIMIT_INTERVAL_USEC, 0));
-
+ log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
+ FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0));
r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
- if (r < 0)
- goto fail;
- } else {
+ } else
r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
- if (r < 0)
- goto fail;
- }
+ if (r < 0)
+ goto fail;
break;
case IPV4ACD_STATE_WAITING_PROBE:
case IPV4ACD_STATE_PROBING:
/* Send a probe */
- r = arp_send_probe(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
+ r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0) {
log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
goto fail;
- } else {
- _cleanup_free_ char *address = NULL;
- union in_addr_union addr = { .in.s_addr = acd->address };
-
- (void) in_addr_to_string(AF_INET, &addr, &address);
- log_ipv4acd(acd, "Probing %s", strna(address));
}
+ log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
+
if (acd->n_iteration < PROBE_NUM - 2) {
ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
@@ -286,12 +271,13 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
_fallthrough_;
case IPV4ACD_STATE_WAITING_ANNOUNCE:
/* Send announcement packet */
- r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
+ r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0) {
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
goto fail;
- } else
- log_ipv4acd(acd, "ANNOUNCE");
+ }
+
+ log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
@@ -317,16 +303,45 @@ fail:
return 0;
}
-static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
- _cleanup_free_ char *address = NULL;
- union in_addr_union addr = { .in.s_addr = acd->address };
+static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
+ assert(acd);
+ assert(arp);
+
+ /* RFC 5227 section 2.1.1.
+ * "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
+ * being performed, where the packet's 'sender IP address' is the address being probed for,
+ * then the host MUST treat this address as being in use by some other host" */
+ if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
+ return true;
+
+ if (announced)
+ /* the TPA matched instead of SPA, this is not a conflict */
+ return false;
+
+ /* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
+ * the packet's 'sender hardware address' is not the hardware address of any of the host's
+ * interfaces, then the host SHOULD similarly treat this as an address conflict" */
+ if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
+ return false; /* not ARP Request, ignoring. */
+ if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
+ return false; /* not ARP Probe, ignoring. */
+ if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
+ return false; /* target IP address does not match, BPF code is broken? */
+
+ if (acd->check_mac_callback &&
+ acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
+ /* sender hardware is one of the host's interfaces, ignoring. */
+ return true;
+
+ return true; /* conflict! */
+}
+static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
assert(acd);
acd->n_conflict++;
- (void) in_addr_to_string(AF_INET, &addr, &address);
- log_ipv4acd(acd, "Conflict on %s (%u)", strna(address), acd->n_conflict);
+ log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
ipv4acd_reset(acd);
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
@@ -365,7 +380,7 @@ static int ipv4acd_on_packet(
case IPV4ACD_STATE_ANNOUNCING:
case IPV4ACD_STATE_RUNNING:
- if (ipv4acd_arp_conflict(acd, &packet)) {
+ if (ipv4acd_arp_conflict(acd, &packet, true)) {
usec_t ts;
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
@@ -373,12 +388,13 @@ static int ipv4acd_on_packet(
/* Defend address */
if (ts > acd->defend_window) {
acd->defend_window = ts + DEFEND_INTERVAL_USEC;
- r = arp_send_announcement(acd->fd, acd->ifindex, acd->address, &acd->mac_addr);
+ r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0) {
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
goto fail;
- } else
- log_ipv4acd(acd, "DEFEND");
+ }
+
+ log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
} else
ipv4acd_on_conflict(acd);
@@ -388,8 +404,8 @@ static int ipv4acd_on_packet(
case IPV4ACD_STATE_WAITING_PROBE:
case IPV4ACD_STATE_PROBING:
case IPV4ACD_STATE_WAITING_ANNOUNCE:
- /* BPF ensures this packet indicates a conflict */
- ipv4acd_on_conflict(acd);
+ if (ipv4acd_arp_conflict(acd, &packet, false))
+ ipv4acd_on_conflict(acd);
break;
default:
@@ -438,12 +454,24 @@ const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
}
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
+ int r;
+
assert_return(acd, -EINVAL);
assert_return(addr, -EINVAL);
- assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+ assert_return(!ether_addr_is_null(addr), -EINVAL);
acd->mac_addr = *addr;
+ if (!sd_ipv4acd_is_running(acd))
+ return 0;
+
+ assert(acd->fd >= 0);
+ r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
+ if (r < 0) {
+ ipv4acd_reset(acd);
+ return r;
+ }
+
return 0;
}
@@ -483,21 +511,51 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *use
return 0;
}
+int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
+ assert_return(acd, -EINVAL);
+
+ acd->check_mac_callback = cb;
+ acd->check_mac_userdata = userdata;
+ return 0;
+}
+
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
+ int r;
+
assert_return(acd, -EINVAL);
assert_return(address, -EINVAL);
- assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+ assert_return(in4_addr_is_set(address), -EINVAL);
- acd->address = address->s_addr;
+ if (in4_addr_equal(&acd->address, address))
+ return 0;
+
+ acd->address = *address;
+
+ if (!sd_ipv4acd_is_running(acd))
+ return 0;
+
+ assert(acd->fd >= 0);
+ r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
+ if (r < 0)
+ goto fail;
+
+ r = ipv4acd_set_next_wakeup(acd, 0, 0);
+ if (r < 0)
+ goto fail;
+ ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
return 0;
+
+fail:
+ ipv4acd_reset(acd);
+ return r;
}
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
assert_return(acd, -EINVAL);
assert_return(address, -EINVAL);
- address->s_addr = acd->address;
+ *address = acd->address;
return 0;
}
@@ -514,16 +572,15 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
assert_return(acd, -EINVAL);
assert_return(acd->event, -EINVAL);
assert_return(acd->ifindex > 0, -EINVAL);
- assert_return(acd->address != 0, -EINVAL);
+ assert_return(in4_addr_is_set(&acd->address), -EINVAL);
assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
- r = arp_network_bind_raw_socket(acd->ifindex, acd->address, &acd->mac_addr);
+ r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0)
return r;
CLOSE_AND_REPLACE(acd->fd, r);
- acd->defend_window = 0;
if (reset_conflicts)
acd->n_conflict = 0;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c
index 8053afee93..7b9d63ca3c 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c
@@ -46,7 +46,10 @@ struct sd_ipv4ll {
be32_t claimed_address;
sd_ipv4ll_callback_t callback;
- void* userdata;
+ void *userdata;
+
+ sd_ipv4ll_check_mac_callback_t check_mac_callback;
+ void *check_mac_userdata;
};
#define log_ipv4ll_errno(ll, error, fmt, ...) \
@@ -60,7 +63,8 @@ struct sd_ipv4ll {
sd_ipv4ll_get_ifname(ll), \
0, fmt, ##__VA_ARGS__)
-static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
+static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
+static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
assert(ll);
@@ -91,6 +95,10 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) {
if (r < 0)
return r;
+ r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll);
+ if (r < 0)
+ return r;
+
*ret = TAKE_PTR(ll);
return 0;
@@ -137,7 +145,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
assert_return(ll, -EINVAL);
assert_return(addr, -EINVAL);
- assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
+ assert_return(!ether_addr_is_null(addr), -EINVAL);
r = sd_ipv4acd_set_mac(ll->acd, addr);
if (r < 0)
@@ -168,6 +176,15 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdat
return 0;
}
+int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) {
+ assert_return(ll, -EINVAL);
+
+ ll->check_mac_callback = cb;
+ ll->check_mac_userdata = userdata;
+
+ return 0;
+}
+
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
assert_return(ll, -EINVAL);
assert_return(address, -EINVAL);
@@ -351,3 +368,14 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
error:
ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
}
+
+static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
+ sd_ipv4ll *ll = userdata;
+
+ assert(ll);
+
+ if (ll->check_mac_callback)
+ return ll->check_mac_callback(ll, mac, ll->check_mac_userdata);
+
+ return 0;
+}
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
index cc9d9f5d9b..e9199deb41 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
@@ -85,6 +85,11 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
SOURCE_DEFER, \
SOURCE_INOTIFY)
+/* This is used to assert that we didn't pass an unexpected source type to event_source_time_prioq_put().
+ * Time sources and ratelimited sources can be passed, so effectively this is the same as the
+ * EVENT_SOURCE_CAN_RATE_LIMIT() macro. */
+#define EVENT_SOURCE_USES_TIME_PRIOQ(t) EVENT_SOURCE_CAN_RATE_LIMIT(t)
+
struct sd_event {
unsigned n_ref;
@@ -1198,6 +1203,7 @@ static int event_source_time_prioq_put(
assert(s);
assert(d);
+ assert(EVENT_SOURCE_USES_TIME_PRIOQ(s->type));
r = prioq_put(d->earliest, s, &s->earliest_index);
if (r < 0)
@@ -2974,6 +2980,7 @@ static int event_arm_timer(
d->needs_rearm = false;
a = prioq_peek(d->earliest);
+ assert(!a || EVENT_SOURCE_USES_TIME_PRIOQ(a->type));
if (!a || a->enabled == SD_EVENT_OFF || time_event_source_next(a) == USEC_INFINITY) {
if (d->fd < 0)
@@ -2991,7 +2998,8 @@ static int event_arm_timer(
}
b = prioq_peek(d->latest);
- assert_se(b && b->enabled != SD_EVENT_OFF);
+ assert(!b || EVENT_SOURCE_USES_TIME_PRIOQ(b->type));
+ assert(b && b->enabled != SD_EVENT_OFF);
t = sleep_between(e, time_event_source_next(a), time_event_source_latest(b));
if (d->next == t)
@@ -3071,6 +3079,8 @@ static int process_timer(
for (;;) {
s = prioq_peek(d->earliest);
+ assert(!s || EVENT_SOURCE_USES_TIME_PRIOQ(s->type));
+
if (!s || time_event_source_next(s) > n)
break;
@@ -3631,6 +3641,8 @@ static int dispatch_exit(sd_event *e) {
assert(e);
p = prioq_peek(e->exit);
+ assert(!p || p->type == SOURCE_EXIT);
+
if (!p || event_source_is_offline(p)) {
e->state = SD_EVENT_FINISHED;
return 0;
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
index d5de935c77..28ae10a198 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
@@ -4,6 +4,11 @@
#include <fcntl.h>
#include <unistd.h>
+#if HAVE_OPENSSL
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+#endif
+
#include "sd-id128.h"
#include "alloc-util.h"
@@ -11,7 +16,9 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
+#if !HAVE_OPENSSL
#include "khash.h"
+#endif
#include "macro.h"
#include "missing_syscall.h"
#include "random-util.h"
@@ -271,13 +278,28 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
}
static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
- _cleanup_(khash_unrefp) khash *h = NULL;
sd_id128_t result;
- const void *p;
- int r;
assert(ret);
+#if HAVE_OPENSSL
+ /* We prefer doing this in-process, since we this means we are not dependent on kernel configuration,
+ * and this also works in locked down container environments. But some distros don't like OpenSSL's
+ * license and its (in-) compatibility with GPL2, hence also support khash */
+ uint8_t md[256/8];
+ if (!HMAC(EVP_sha256(),
+ &base, sizeof(base),
+ (const unsigned char*) &app_id, sizeof(app_id),
+ md, NULL))
+ return -ENOTRECOVERABLE;
+
+ /* Take only the first half. */
+ memcpy(&result, md, MIN(sizeof(md), sizeof(result)));
+#else
+ _cleanup_(khash_unrefp) khash *h = NULL;
+ const void *p;
+ int r;
+
r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
if (r < 0)
return r;
@@ -292,6 +314,7 @@ static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret)
/* We chop off the trailing 16 bytes */
memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
+#endif
*ret = id128_make_v4_uuid(result);
return 0;
diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h b/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h
index 1e89a81b31..3213db553c 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h
@@ -36,11 +36,13 @@ enum {
typedef struct sd_ipv4acd sd_ipv4acd;
typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata);
+typedef int (*sd_ipv4acd_check_mac_callback_t)(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
int sd_ipv4acd_detach_event(sd_ipv4acd *acd);
int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority);
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address);
int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata);
+int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata);
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd);
diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h b/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h
index bf5596ab61..27263bc837 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h
@@ -36,11 +36,13 @@ enum {
typedef struct sd_ipv4ll sd_ipv4ll;
typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata);
+typedef int (*sd_ipv4ll_check_mac_callback_t)(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata);
int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
+int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata);
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll);
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index 2a3a0e31a1..4bb98f9342 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -8,6 +8,7 @@
#include "escape.h"
#include "hexdecoct.h"
#include "macro.h"
+#include "strv.h"
#include "utf8.h"
int cescape_char(char c, char *buf) {
@@ -288,10 +289,12 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
return r;
}
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
- char *r, *t;
+ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
+ _cleanup_free_ char *ans = NULL;
+ char *t;
const char *f;
size_t pl;
+ int r;
assert(s);
assert(ret);
@@ -300,18 +303,17 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
pl = strlen_ptr(prefix);
- r = new(char, pl+length+1);
- if (!r)
+ ans = new(char, pl+length+1);
+ if (!ans)
return -ENOMEM;
if (prefix)
- memcpy(r, prefix, pl);
+ memcpy(ans, prefix, pl);
- for (f = s, t = r + pl; f < s + length; f++) {
+ for (f = s, t = ans + pl; f < s + length; f++) {
size_t remaining;
bool eight_bit = false;
char32_t u;
- int k;
remaining = s + length - f;
assert(remaining > 0);
@@ -329,23 +331,21 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
continue;
}
- free(r);
return -EINVAL;
}
- k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
- if (k < 0) {
+ r = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
+ if (r < 0) {
if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
continue;
}
- free(r);
- return k;
+ return r;
}
- f += k;
+ f += r;
if (eight_bit)
/* One byte? Set directly as specified */
*(t++) = u;
@@ -356,8 +356,9 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
*t = 0;
- *ret = r;
- return t - r;
+ assert(t >= ans); /* Let static analyzers know that the answer is non-negative. */
+ *ret = TAKE_PTR(ans);
+ return t - *ret;
}
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
@@ -542,3 +543,23 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
return str_realloc(buf);
}
+
+char* quote_command_line(char **argv) {
+ _cleanup_free_ char *result = NULL;
+
+ assert(argv);
+
+ char **a;
+ STRV_FOREACH(a, argv) {
+ _cleanup_free_ char *t = NULL;
+
+ t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY);
+ if (!t)
+ return NULL;
+
+ if (!strextend_with_separator(&result, " ", t))
+ return NULL;
+ }
+
+ return TAKE_PTR(result);
+}
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 907b572bd4..d490510deb 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -45,14 +45,15 @@ char* cescape(const char *s);
char* cescape_length(const char *s, size_t n);
int cescape_char(char c, char *buf);
-int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
-static inline int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
+
+ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
+static inline ssize_t cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
return cunescape_length_with_prefix(s, length, NULL, flags, ret);
}
-static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+static inline ssize_t cunescape(const char *s, UnescapeFlags flags, char **ret) {
return cunescape_length(s, strlen(s), flags, ret);
}
-int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
typedef enum XEscapeFlags {
XESCAPE_8_BIT = 1 << 0,
@@ -68,3 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
char* shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
+char* quote_command_line(char **argv);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index 3925810768..e660ac2c6f 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -37,6 +37,16 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b)
return memcmp(a->bytes, b->bytes, a->length);
}
+static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
+ assert(p);
+ assert(state);
+
+ siphash24_compress(&p->length, sizeof(p->length), state);
+ siphash24_compress(p->bytes, p->length, state);
+}
+
+DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
+
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
assert(addr);
assert(buffer);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index cf8afff356..794fc55bb8 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -23,7 +23,9 @@ struct hw_addr_data {
#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
-/* Use only as function argument, never stand-alone! */
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 ยง6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){})
#define HW_ADDR_NULL ((const struct hw_addr_data){})
@@ -36,6 +38,8 @@ static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
return hw_addr_equal(addr, &HW_ADDR_NULL);
}
+extern const struct hash_ops hw_addr_hash_ops;
+
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c
index 2c14b6f0cf..9f9bb0c791 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.c
+++ b/src/libnm-systemd-shared/src/basic/extract-word.c
@@ -27,6 +27,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
assert(p);
assert(ret);
+ assert(!FLAGS_SET(flags, EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE));
/* Bail early if called after last value or with no input */
if (!*p)
@@ -50,7 +51,8 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
goto finish_force_terminate;
else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
- (*p)++;
+ if (!(flags & EXTRACT_RETAIN_SEPARATORS))
+ (*p)++;
goto finish_force_next;
}
} else {
@@ -123,48 +125,58 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
return -EINVAL;
} else if (c == quote) { /* found the end quote */
quote = 0;
- break;
+ if (flags & EXTRACT_UNQUOTE)
+ break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
backslash = true;
break;
- } else {
- if (!GREEDY_REALLOC(s, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
}
+
+ if (!GREEDY_REALLOC(s, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+
+ if (quote == 0)
+ break;
}
} else {
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
- else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
+ else if (IN_SET(c, '\'', '"') && (flags & (EXTRACT_KEEP_QUOTE | EXTRACT_UNQUOTE))) {
quote = c;
- break;
+ if (flags & EXTRACT_UNQUOTE)
+ break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
backslash = true;
break;
} else if (strchr(separators, c)) {
if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
- (*p)++;
+ if (!(flags & EXTRACT_RETAIN_SEPARATORS))
+ (*p)++;
goto finish_force_next;
}
- /* Skip additional coalesced separators. */
- for (;; (*p)++, c = **p) {
- if (c == 0)
- goto finish_force_terminate;
- if (!strchr(separators, c))
- break;
- }
+ if (!(flags & EXTRACT_RETAIN_SEPARATORS))
+ /* Skip additional coalesced separators. */
+ for (;; (*p)++, c = **p) {
+ if (c == 0)
+ goto finish_force_terminate;
+ if (!strchr(separators, c))
+ break;
+ }
goto finish;
- } else {
- if (!GREEDY_REALLOC(s, sz+2))
- return -ENOMEM;
-
- s[sz++] = c;
}
+
+ if (!GREEDY_REALLOC(s, sz+2))
+ return -ENOMEM;
+
+ s[sz++] = c;
+
+ if (quote != 0)
+ break;
}
}
}
diff --git a/src/libnm-systemd-shared/src/basic/extract-word.h b/src/libnm-systemd-shared/src/basic/extract-word.h
index 0e9e77e93d..c82ad761ef 100644
--- a/src/libnm-systemd-shared/src/basic/extract-word.h
+++ b/src/libnm-systemd-shared/src/basic/extract-word.h
@@ -8,9 +8,11 @@ typedef enum ExtractFlags {
EXTRACT_CUNESCAPE = 1 << 1, /* Unescape known escape sequences. */
EXTRACT_UNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */
EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */
- EXTRACT_UNQUOTE = 1 << 4, /* Remove quoting with "" and ''. */
- EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */
- EXTRACT_RETAIN_ESCAPE = 1 << 6, /* Treat escape character '\' as any other character without special meaning */
+ EXTRACT_KEEP_QUOTE = 1 << 4, /* Ignore separators in quoting with "" and ''. */
+ EXTRACT_UNQUOTE = 1 << 5, /* Ignore separators in quoting with "" and '', and remove the quotes. */
+ EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 6, /* Don't treat multiple adjacent separators as one */
+ EXTRACT_RETAIN_ESCAPE = 1 << 7, /* Treat escape character '\' as any other character without special meaning */
+ EXTRACT_RETAIN_SEPARATORS = 1 << 8, /* Do not advance the original string pointer past the separator(s) */
/* Note that if no flags are specified, escaped escape characters will be silently stripped. */
} ExtractFlags;
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index 1a873601b2..6b6457dbc2 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -2,19 +2,20 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/btrfs.h>
+#include <linux/magic.h>
+#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h"
-#include "copy.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "io-util.h"
#include "macro.h"
-#include "memfd-util.h"
#include "missing_fcntl.h"
#include "missing_syscall.h"
#include "parse-util.h"
@@ -207,10 +208,9 @@ static int get_max_fd(void) {
return (int) (m - 1);
}
-int close_all_fds(const int except[], size_t n_except) {
+int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
static bool have_close_range = true; /* Assume we live in the future */
_cleanup_closedir_ DIR *d = NULL;
- struct dirent *de;
int r = 0;
assert(n_except == 0 || except);
@@ -226,129 +226,104 @@ int close_all_fds(const int except[], size_t n_except) {
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
- return 1;
+ return 0;
- if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
+ if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
+ have_close_range = false;
+ else
return -errno;
- have_close_range = false;
} else {
- _cleanup_free_ int *sorted_malloc = NULL;
- size_t n_sorted;
- int *sorted;
-
- assert(n_except < SIZE_MAX);
- n_sorted = n_except + 1;
-
- if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
- sorted = sorted_malloc = new(int, n_sorted);
- else
- sorted = newa(int, n_sorted);
-
- if (sorted) {
- int c = 0;
-
- memcpy(sorted, except, n_except * sizeof(int));
+ typesafe_qsort(except, n_except, cmp_int);
- /* Let's add fd 2 to the list of fds, to simplify the loop below, as this
- * allows us to cover the head of the array the same way as the body */
- sorted[n_sorted-1] = 2;
+ for (size_t i = 0; i < n_except; i++) {
+ int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */
+ int end = MAX(except[i], 2);
- typesafe_qsort(sorted, n_sorted, cmp_int);
+ assert(end >= start);
- for (size_t i = 0; i < n_sorted-1; i++) {
- int start, end;
-
- start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
- end = MAX(sorted[i+1], 2);
-
- assert(end >= start);
-
- if (end - start <= 1)
- continue;
-
- /* Close everything between the start and end fds (both of which shall stay open) */
- if (close_range(start + 1, end - 1, 0) < 0) {
- if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
- return -errno;
+ if (end - start <= 1)
+ continue;
+ /* Close everything between the start and end fds (both of which shall stay open) */
+ if (close_range(start + 1, end - 1, 0) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
- break;
- }
-
- c += end - start - 1;
+ else
+ return -errno;
+ goto opendir_fallback;
}
+ }
- if (have_close_range) {
- /* The loop succeeded. Let's now close everything beyond the end */
+ /* The loop succeeded. Let's now close everything beyond the end */
- if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
- return c;
+ if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */
+ return 0;
- if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
- return c + 1;
+ int start = MAX(except[n_except-1], 2);
- if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
- return -errno;
+ if (close_range(start + 1, -1, 0) >= 0)
+ return 0;
- have_close_range = false;
- }
- }
+ if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
+ have_close_range = false;
+ else
+ return -errno;
}
-
- /* Fallback on OOM or if close_range() is not supported */
}
- d = opendir("/proc/self/fd");
- if (!d) {
- int fd, max_fd;
+ /* Fallback for when close_range() is not supported */
+ opendir_fallback:
+ d = allow_alloc ? opendir("/proc/self/fd") : NULL;
+ if (d) {
+ struct dirent *de;
- /* When /proc isn't available (for example in chroots) the fallback is brute forcing through
- * the fd table */
+ FOREACH_DIRENT(de, d, return -errno) {
+ int fd = -1, q;
- max_fd = get_max_fd();
- if (max_fd < 0)
- return max_fd;
+ if (safe_atoi(de->d_name, &fd) < 0)
+ /* Let's better ignore this, just in case */
+ continue;
- /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
- * spin the CPU for a long time. */
- if (max_fd > MAX_FD_LOOP_LIMIT)
- return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
- "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
- max_fd);
+ if (fd < 3)
+ continue;
- for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
- int q;
+ if (fd == dirfd(d))
+ continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
- if (q < 0 && q != -EBADF && r >= 0)
+ if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
r = q;
}
return r;
}
- FOREACH_DIRENT(de, d, return -errno) {
- int fd = -1, q;
+ /* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by
+ * brute-forcing through the file descriptor table. */
- if (safe_atoi(de->d_name, &fd) < 0)
- /* Let's better ignore this, just in case */
- continue;
+ int max_fd = get_max_fd();
+ if (max_fd < 0)
+ return max_fd;
- if (fd < 3)
- continue;
+ /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
+ * spin the CPU for a long time. */
+ if (max_fd > MAX_FD_LOOP_LIMIT)
+ return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+ "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
+ max_fd);
- if (fd == dirfd(d))
- continue;
+ for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
+ int q;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
- if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
+ if (q < 0 && q != -EBADF && r >= 0)
r = q;
}
@@ -520,343 +495,6 @@ int move_fd(int from, int to, int cloexec) {
return to;
}
-int acquire_data_fd(const void *data, size_t size, unsigned flags) {
-
- _cleanup_close_pair_ int pipefds[2] = { -1, -1 };
- char pattern[] = "/dev/shm/data-fd-XXXXXX";
- _cleanup_close_ int fd = -1;
- int isz = 0, r;
- ssize_t n;
- off_t f;
-
- assert(data || size == 0);
-
- /* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
- * complex than I wish it was. But here's why:
- *
- * a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
- * read-only. Unfortunately they require kernel 3.17, and โ€“ at the time of writing โ€“ we still support 3.14.
- *
- * b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
- * a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
- * clients can only bump their size to a system-wide limit, which might be quite low.
- *
- * c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
- * earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
- * /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
- *
- * d) Finally, we try creating a regular file in /dev/shm, which we then delete.
- *
- * It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
- * figure. */
-
- if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
- /* As a special case, return /dev/null if we have been called for an empty data block */
- r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (r < 0)
- return -errno;
-
- return r;
- }
-
- if ((flags & ACQUIRE_NO_MEMFD) == 0) {
- fd = memfd_new("data-fd");
- if (fd < 0)
- goto try_pipe;
-
- n = write(fd, data, size);
- if (n < 0)
- return -errno;
- if ((size_t) n != size)
- return -EIO;
-
- f = lseek(fd, 0, SEEK_SET);
- if (f != 0)
- return -errno;
-
- r = memfd_set_sealed(fd);
- if (r < 0)
- return r;
-
- return TAKE_FD(fd);
- }
-
-try_pipe:
- if ((flags & ACQUIRE_NO_PIPE) == 0) {
- if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
- return -errno;
-
- isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
- if (isz < 0)
- return -errno;
-
- if ((size_t) isz < size) {
- isz = (int) size;
- if (isz < 0 || (size_t) isz != size)
- return -E2BIG;
-
- /* Try to bump the pipe size */
- (void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
-
- /* See if that worked */
- isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
- if (isz < 0)
- return -errno;
-
- if ((size_t) isz < size)
- goto try_dev_shm;
- }
-
- n = write(pipefds[1], data, size);
- if (n < 0)
- return -errno;
- if ((size_t) n != size)
- return -EIO;
-
- (void) fd_nonblock(pipefds[0], false);
-
- return TAKE_FD(pipefds[0]);
- }
-
-try_dev_shm:
- if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
- fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
- if (fd < 0)
- goto try_dev_shm_without_o_tmpfile;
-
- n = write(fd, data, size);
- if (n < 0)
- return -errno;
- if ((size_t) n != size)
- return -EIO;
-
- /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
- return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
- }
-
-try_dev_shm_without_o_tmpfile:
- if ((flags & ACQUIRE_NO_REGULAR) == 0) {
- fd = mkostemp_safe(pattern);
- if (fd < 0)
- return fd;
-
- n = write(fd, data, size);
- if (n < 0) {
- r = -errno;
- goto unlink_and_return;
- }
- if ((size_t) n != size) {
- r = -EIO;
- goto unlink_and_return;
- }
-
- /* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
- r = open(pattern, O_RDONLY|O_CLOEXEC);
- if (r < 0)
- r = -errno;
-
- unlink_and_return:
- (void) unlink(pattern);
- return r;
- }
-
- return -EOPNOTSUPP;
-}
-
-/* When the data is smaller or equal to 64K, try to place the copy in a memfd/pipe */
-#define DATA_FD_MEMORY_LIMIT (64U*1024U)
-
-/* If memfd/pipe didn't work out, then let's use a file in /tmp up to a size of 1M. If it's large than that use /var/tmp instead. */
-#define DATA_FD_TMP_LIMIT (1024U*1024U)
-
-int fd_duplicate_data_fd(int fd) {
-
- _cleanup_close_ int copy_fd = -1, tmp_fd = -1;
- _cleanup_free_ void *remains = NULL;
- size_t remains_size = 0;
- const char *td;
- struct stat st;
- int r;
-
- /* Creates a 'data' fd from the specified source fd, containing all the same data in a read-only fashion, but
- * independent of it (i.e. the source fd can be closed and unmounted after this call succeeded). Tries to be
- * somewhat smart about where to place the data. In the best case uses a memfd(). If memfd() are not supported
- * uses a pipe instead. For larger data will use an unlinked file in /tmp, and for even larger data one in
- * /var/tmp. */
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- /* For now, let's only accept regular files, sockets, pipes and char devices */
- if (S_ISDIR(st.st_mode))
- return -EISDIR;
- if (S_ISLNK(st.st_mode))
- return -ELOOP;
- if (!S_ISREG(st.st_mode) && !S_ISSOCK(st.st_mode) && !S_ISFIFO(st.st_mode) && !S_ISCHR(st.st_mode))
- return -EBADFD;
-
- /* If we have reason to believe the data is bounded in size, then let's use memfds or pipes as backing fd. Note
- * that we use the reported regular file size only as a hint, given that there are plenty special files in
- * /proc and /sys which report a zero file size but can be read from. */
-
- if (!S_ISREG(st.st_mode) || st.st_size < DATA_FD_MEMORY_LIMIT) {
-
- /* Try a memfd first */
- copy_fd = memfd_new("data-fd");
- if (copy_fd >= 0) {
- off_t f;
-
- r = copy_bytes(fd, copy_fd, DATA_FD_MEMORY_LIMIT, 0);
- if (r < 0)
- return r;
-
- f = lseek(copy_fd, 0, SEEK_SET);
- if (f != 0)
- return -errno;
-
- if (r == 0) {
- /* Did it fit into the limit? If so, we are done. */
- r = memfd_set_sealed(copy_fd);
- if (r < 0)
- return r;
-
- return TAKE_FD(copy_fd);
- }
-
- /* Hmm, pity, this didn't fit. Let's fall back to /tmp then, see below */
-
- } else {
- _cleanup_(close_pairp) int pipefds[2] = { -1, -1 };
- int isz;
-
- /* If memfds aren't available, use a pipe. Set O_NONBLOCK so that we will get EAGAIN rather
- * then block indefinitely when we hit the pipe size limit */
-
- if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
- return -errno;
-
- isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
- if (isz < 0)
- return -errno;
-
- /* Try to enlarge the pipe size if necessary */
- if ((size_t) isz < DATA_FD_MEMORY_LIMIT) {
-
- (void) fcntl(pipefds[1], F_SETPIPE_SZ, DATA_FD_MEMORY_LIMIT);
-
- isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
- if (isz < 0)
- return -errno;
- }
-
- if ((size_t) isz >= DATA_FD_MEMORY_LIMIT) {
-
- r = copy_bytes_full(fd, pipefds[1], DATA_FD_MEMORY_LIMIT, 0, &remains, &remains_size, NULL, NULL);
- if (r < 0 && r != -EAGAIN)
- return r; /* If we get EAGAIN it could be because of the source or because of
- * the destination fd, we can't know, as sendfile() and friends won't
- * tell us. Hence, treat this as reason to fall back, just to be
- * sure. */
- if (r == 0) {
- /* Everything fit in, yay! */
- (void) fd_nonblock(pipefds[0], false);
-
- return TAKE_FD(pipefds[0]);
- }
-
- /* Things didn't fit in. But we read data into the pipe, let's remember that, so that
- * when writing the new file we incorporate this first. */
- copy_fd = TAKE_FD(pipefds[0]);
- }
- }
- }
-
- /* If we have reason to believe this will fit fine in /tmp, then use that as first fallback. */
- if ((!S_ISREG(st.st_mode) || st.st_size < DATA_FD_TMP_LIMIT) &&
- (DATA_FD_MEMORY_LIMIT + remains_size) < DATA_FD_TMP_LIMIT) {
- off_t f;
-
- tmp_fd = open_tmpfile_unlinkable(NULL /* NULL as directory means /tmp */, O_RDWR|O_CLOEXEC);
- if (tmp_fd < 0)
- return tmp_fd;
-
- if (copy_fd >= 0) {
- /* If we tried a memfd/pipe first and it ended up being too large, then copy this into the
- * temporary file first. */
-
- r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, 0);
- if (r < 0)
- return r;
-
- assert(r == 0);
- }
-
- if (remains_size > 0) {
- /* If there were remaining bytes (i.e. read into memory, but not written out yet) from the
- * failed copy operation, let's flush them out next. */
-
- r = loop_write(tmp_fd, remains, remains_size, false);
- if (r < 0)
- return r;
- }
-
- r = copy_bytes(fd, tmp_fd, DATA_FD_TMP_LIMIT - DATA_FD_MEMORY_LIMIT - remains_size, COPY_REFLINK);
- if (r < 0)
- return r;
- if (r == 0)
- goto finish; /* Yay, it fit in */
-
- /* It didn't fit in. Let's not forget to use what we already used */
- f = lseek(tmp_fd, 0, SEEK_SET);
- if (f != 0)
- return -errno;
-
- CLOSE_AND_REPLACE(copy_fd, tmp_fd);
-
- remains = mfree(remains);
- remains_size = 0;
- }
-
- /* As last fallback use /var/tmp */
- r = var_tmp_dir(&td);
- if (r < 0)
- return r;
-
- tmp_fd = open_tmpfile_unlinkable(td, O_RDWR|O_CLOEXEC);
- if (tmp_fd < 0)
- return tmp_fd;
-
- if (copy_fd >= 0) {
- /* If we tried a memfd/pipe first, or a file in /tmp, and it ended up being too large, than copy this
- * into the temporary file first. */
- r = copy_bytes(copy_fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
- if (r < 0)
- return r;
-
- assert(r == 0);
- }
-
- if (remains_size > 0) {
- /* Then, copy in any read but not yet written bytes. */
- r = loop_write(tmp_fd, remains, remains_size, false);
- if (r < 0)
- return r;
- }
-
- /* Copy in the rest */
- r = copy_bytes(fd, tmp_fd, UINT64_MAX, COPY_REFLINK);
- if (r < 0)
- return r;
-
- assert(r == 0);
-
-finish:
- /* Now convert the O_RDWR file descriptor into an O_RDONLY one (and as side effect seek to the beginning of the
- * file again */
-
- return fd_reopen(tmp_fd, O_RDONLY|O_CLOEXEC);
-}
-
int fd_move_above_stdio(int fd) {
int flags, copy;
PROTECT_ERRNO;
@@ -1057,3 +695,20 @@ int read_nr_open(void) {
/* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */
return 1024 * 1024;
}
+
+/* This is here because it's fd-related and is called from sd-journal code. Other btrfs-related utilities are
+ * in src/shared, but libsystemd must not link to libsystemd-shared, see docs/ARCHITECTURE.md. */
+int btrfs_defrag_fd(int fd) {
+ int r;
+
+ assert(fd >= 0);
+
+ r = fd_verify_regular(fd);
+ if (r < 0)
+ return r;
+
+ if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index aa8e082b38..61b6684cb3 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -56,7 +56,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec);
-int close_all_fds(const int except[], size_t n_except);
+int close_all_fds_full(int except[], size_t n_except, bool allow_alloc);
+static inline int close_all_fds(int except[], size_t n_except) {
+ return close_all_fds_full(except, n_except, true);
+}
int same_fd(int a, int b);
@@ -76,10 +79,6 @@ enum {
ACQUIRE_NO_REGULAR = 1 << 4,
};
-int acquire_data_fd(const void *data, size_t size, unsigned flags);
-
-int fd_duplicate_data_fd(int fd);
-
int fd_move_above_stdio(int fd);
int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
@@ -107,5 +106,5 @@ static inline int make_null_stdio(void) {
int fd_reopen(int fd, int flags);
-
int read_nr_open(void);
+int btrfs_defrag_fd(int fd);
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 99a44fdea2..19fe6e214c 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -522,18 +522,17 @@ int read_full_stream_full(
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
- size_t n, n_next, l;
+ size_t n, n_next = 0, l;
int fd, r;
assert(f);
assert(ret_contents);
assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
+ assert(size != SIZE_MAX || !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER));
- if (offset != UINT64_MAX && offset > LONG_MAX)
+ if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
return -ERANGE;
- n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */
-
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
* fmemopen()), let's optimize our buffering */
@@ -543,20 +542,20 @@ int read_full_stream_full(
return -errno;
if (S_ISREG(st.st_mode)) {
- if (size == SIZE_MAX) {
+
+ /* Try to start with the right file size if we shall read the file in full. Note
+ * that we increase the size to read here by one, so that the first read attempt
+ * already makes us notice the EOF. If the reported size of the file is zero, we
+ * avoid this logic however, since quite likely it might be a virtual file in procfs
+ * that all report a zero file size. */
+
+ if (st.st_size > 0 &&
+ (size == SIZE_MAX || FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER))) {
+
uint64_t rsize =
LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset);
- /* Safety check */
- if (rsize > READ_FULL_BYTES_MAX)
- return -E2BIG;
-
- /* Start with the right file size. Note that we increase the size to read
- * here by one, so that the first read attempt already makes us notice the
- * EOF. If the reported size of the file is zero, we avoid this logic
- * however, since quite likely it might be a virtual file in procfs that all
- * report a zero file size. */
- if (st.st_size > 0)
+ if (rsize < SIZE_MAX) /* overflow check */
n_next = rsize + 1;
}
@@ -565,6 +564,17 @@ int read_full_stream_full(
}
}
+ /* If we don't know how much to read, figure it out now. If we shall read a part of the file, then
+ * allocate the requested size. If we shall load the full file start with LINE_MAX. Note that if
+ * READ_FULL_FILE_FAIL_WHEN_LARGER we consider the specified size a safety limit, and thus also start
+ * with LINE_MAX, under assumption the file is most likely much shorter. */
+ if (n_next == 0)
+ n_next = size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) ? size : LINE_MAX;
+
+ /* Never read more than we need to determine that our own limit is hit */
+ if (n_next > READ_FULL_BYTES_MAX)
+ n_next = READ_FULL_BYTES_MAX + 1;
+
if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0)
return -errno;
@@ -573,6 +583,11 @@ int read_full_stream_full(
char *t;
size_t k;
+ /* If we shall fail when reading overly large data, then read exactly one byte more than the
+ * specified size at max, since that'll tell us if there's anymore data beyond the limit*/
+ if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
+ n_next = size + 1;
+
if (flags & READ_FULL_FILE_SECURE) {
t = malloc(n_next + 1);
if (!t) {
@@ -606,14 +621,18 @@ int read_full_stream_full(
if (feof(f))
break;
- if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
+ if (size != SIZE_MAX && !FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER)) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
assert(l == size);
break;
}
assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
- /* Safety check */
+ if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > size) {
+ r = -E2BIG;
+ goto finalize;
+ }
+
if (n >= READ_FULL_BYTES_MAX) {
r = -E2BIG;
goto finalize;
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index c28b17fef5..af797cfafd 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -39,6 +39,7 @@ typedef enum {
READ_FULL_FILE_UNHEX = 1 << 2, /* hex decode what we read */
READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, /* if socket inode, connect to it and read off it */
+ READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */
} ReadFullFileFlags;
int fopen_unlocked(const char *path, const char *options, FILE **ret);
diff --git a/src/libnm-systemd-shared/src/basic/format-util.h b/src/libnm-systemd-shared/src/basic/format-util.h
index b7e18768e3..579f231897 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.h
+++ b/src/libnm-systemd-shared/src/basic/format-util.h
@@ -74,16 +74,17 @@ typedef enum {
#define FORMAT_BYTES_MAX 16U
-char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
+char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) _warn_unused_result_;
+_warn_unused_result_
static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
}
-static inline char *format_bytes_cgroup_protection(char *buf, size_t l, uint64_t t) {
- if (t == CGROUP_LIMIT_MAX) {
- (void) snprintf(buf, l, "%s", "infinity");
- return buf;
- }
- return format_bytes(buf, l, t);
-}
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 ยง6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define FORMAT_BYTES(t) format_bytes((char[FORMAT_BYTES_MAX]){}, FORMAT_BYTES_MAX, t)
+#define FORMAT_BYTES_FULL(t, flag) format_bytes_full((char[FORMAT_BYTES_MAX]){}, FORMAT_BYTES_MAX, t, flag)
+
+#define FORMAT_BYTES_CGROUP_PROTECTION(t) (t == CGROUP_LIMIT_MAX ? "infinity" : FORMAT_BYTES(t))
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 5fe8fbab98..28c7247e05 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -8,7 +8,6 @@
#include <unistd.h>
#include "alloc-util.h"
-#include "blockdev-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -581,8 +580,6 @@ int get_files_in_directory(const char *path, char ***list) {
return -errno;
FOREACH_DIRENT_ALL(de, d, return -errno) {
- dirent_ensure_type(d, de);
-
if (!dirent_is_file(de))
continue;
@@ -745,7 +742,8 @@ bool unsafe_transition(const struct stat *a, const struct stat *b) {
}
static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
- _cleanup_free_ char *n1 = NULL, *n2 = NULL;
+ _cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL;
+ struct stat st;
if (!FLAGS_SET(flags, CHASE_WARN))
return -ENOLINK;
@@ -753,9 +751,14 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags)
(void) fd_get_path(a, &n1);
(void) fd_get_path(b, &n2);
+ if (fstat(a, &st) == 0)
+ user_a = uid_to_name(st.st_uid);
+ if (fstat(b, &st) == 0)
+ user_b = uid_to_name(st.st_uid);
+
return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
- "Detected unsafe path transition %s %s %s during canonicalization of %s.",
- strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path);
+ "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.",
+ strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path);
}
static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
@@ -1375,18 +1378,39 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
}
int fsync_directory_of_file(int fd) {
- _cleanup_free_ char *path = NULL;
_cleanup_close_ int dfd = -1;
struct stat st;
int r;
assert(fd >= 0);
- /* We only reasonably can do this for regular files and directories, hence check for that */
+ /* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check
+ * for the inode type first */
if (fstat(fd, &st) < 0)
return -errno;
- if (S_ISREG(st.st_mode)) {
+ if (S_ISDIR(st.st_mode)) {
+ dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
+ if (dfd < 0)
+ return -errno;
+
+ } else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other
+ * types check O_PATH flag */
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0)
+ return -errno;
+
+ if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case
+ * we can sensibly do what is requested. Otherwise this refers
+ * to a socket, fifo or device node, where the concept of a
+ * containing directory doesn't make too much sense. */
+ return -ENOTTY;
+ }
+
+ if (dfd < 0) {
+ _cleanup_free_ char *path = NULL;
r = fd_get_path(fd, &path);
if (r < 0) {
@@ -1409,13 +1433,7 @@ int fsync_directory_of_file(int fd) {
dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
if (dfd < 0)
return dfd;
-
- } else if (S_ISDIR(st.st_mode)) {
- dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
- if (dfd < 0)
- return -errno;
- } else
- return -ENOTTY;
+ }
if (fsync(dfd) < 0)
return -errno;
@@ -1466,12 +1484,56 @@ int fsync_path_at(int at_fd, const char *path) {
return 0;
}
+int fsync_parent_at(int at_fd, const char *path) {
+ _cleanup_close_ int opened_fd = -1;
+
+ if (isempty(path)) {
+ if (at_fd != AT_FDCWD)
+ return fsync_directory_of_file(at_fd);
+
+ opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ if (opened_fd < 0)
+ return -errno;
+
+ if (fsync(opened_fd) < 0)
+ return -errno;
+
+ return 0;
+ }
+
+ opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (opened_fd < 0)
+ return -errno;
+
+ return fsync_directory_of_file(opened_fd);
+}
+
+int fsync_path_and_parent_at(int at_fd, const char *path) {
+ _cleanup_close_ int opened_fd = -1;
+
+ if (isempty(path)) {
+ if (at_fd != AT_FDCWD)
+ return fsync_full(at_fd);
+
+ opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ } else
+ opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
+ if (opened_fd < 0)
+ return -errno;
+
+ return fsync_full(opened_fd);
+}
+
int syncfs_path(int atfd, const char *path) {
_cleanup_close_ int fd = -1;
- assert(path);
+ if (isempty(path)) {
+ if (atfd != AT_FDCWD)
+ return syncfs(atfd) < 0 ? -errno : 0;
- fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK);
+ fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+ } else
+ fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return -errno;
@@ -1504,91 +1566,6 @@ int open_parent(const char *path, int flags, mode_t mode) {
return fd;
}
-static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
- _cleanup_free_ char *p = NULL, *uuids = NULL;
- _cleanup_closedir_ DIR *d = NULL;
- int r, found_encrypted = false;
-
- assert(sysfs_path);
-
- if (depth_left == 0)
- return -EINVAL;
-
- p = path_join(sysfs_path, "dm/uuid");
- if (!p)
- return -ENOMEM;
-
- r = read_one_line_file(p, &uuids);
- if (r != -ENOENT) {
- if (r < 0)
- return r;
-
- /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
- if (startswith(uuids, "CRYPT-"))
- return true;
- }
-
- /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
- * subdir. */
-
- p = mfree(p);
- p = path_join(sysfs_path, "slaves");
- if (!p)
- return -ENOMEM;
-
- d = opendir(p);
- if (!d) {
- if (errno == ENOENT) /* Doesn't have underlying devices */
- return false;
-
- return -errno;
- }
-
- for (;;) {
- _cleanup_free_ char *q = NULL;
- struct dirent *de;
-
- errno = 0;
- de = readdir_no_dot(d);
- if (!de) {
- if (errno != 0)
- return -errno;
-
- break; /* No more underlying devices */
- }
-
- q = path_join(p, de->d_name);
- if (!q)
- return -ENOMEM;
-
- r = blockdev_is_encrypted(q, depth_left - 1);
- if (r < 0)
- return r;
- if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
- return false;
-
- found_encrypted = true;
- }
-
- return found_encrypted;
-}
-
-int path_is_encrypted(const char *path) {
- char p[SYS_BLOCK_PATH_MAX(NULL)];
- dev_t devt;
- int r;
-
- r = get_block_device(path, &devt);
- if (r < 0)
- return r;
- if (r == 0) /* doesn't have a block device */
- return false;
-
- xsprintf_sys_block_path(p, NULL, devt);
-
- return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
-}
-
int conservative_renameat(
int olddirfd, const char *oldpath,
int newdirfd, const char *newpath) {
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index 85bdea64df..d612cbe404 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -140,13 +140,13 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
int fsync_directory_of_file(int fd);
int fsync_full(int fd);
int fsync_path_at(int at_fd, const char *path);
+int fsync_parent_at(int at_fd, const char *path);
+int fsync_path_and_parent_at(int at_fd, const char *path);
int syncfs_path(int atfd, const char *path);
int open_parent(const char *path, int flags, mode_t mode);
-int path_is_encrypted(const char *path);
-
int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
static inline int conservative_rename(const char *oldpath, const char *newpath) {
return conservative_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath);
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 34fed69d99..0decbb04e1 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -112,7 +112,7 @@ assert_cc(IDX_FIRST == _IDX_SWAP_END);
assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST);
/* Storage space for the "swap" buckets.
- * All entry types can fit into a ordered_hashmap_entry. */
+ * All entry types can fit into an ordered_hashmap_entry. */
struct swap_entries {
struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN];
};
@@ -1761,6 +1761,9 @@ char** _hashmap_get_strv(HashmapBase *h) {
Iterator i;
unsigned idx, n;
+ if (!h)
+ return new0(char*, 1);
+
sv = new(char*, n_entries(h)+1);
if (!sv)
return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index f7ade2e774..eafc08f658 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -224,7 +224,7 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo
return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value);
}
-/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
+/* Since merging data from an OrderedHashmap into a Hashmap or vice-versa
* should just work, allow this by having looser type-checking here. */
int _hashmap_merge(Hashmap *h, Hashmap *other);
#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c
index da1add7c76..8c83a0e71a 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.c
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c
@@ -565,38 +565,79 @@ int unbase64char(char c) {
return -EINVAL;
}
-ssize_t base64mem(const void *p, size_t l, char **out) {
- char *r, *z;
+static void maybe_line_break(char **x, char *start, size_t line_break) {
+ size_t n;
+
+ assert(x);
+ assert(*x);
+ assert(start);
+ assert(*x >= start);
+
+ if (line_break == SIZE_MAX)
+ return;
+
+ n = *x - start;
+
+ if (n % (line_break + 1) == line_break)
+ *((*x)++) = '\n';
+}
+
+ssize_t base64mem_full(
+ const void *p,
+ size_t l,
+ size_t line_break,
+ char **out) {
+
const uint8_t *x;
+ char *r, *z;
+ size_t m;
assert(p || l == 0);
assert(out);
+ assert(line_break > 0);
/* three input bytes makes four output bytes, padding is added so we must round up */
- z = r = malloc(4 * (l + 2) / 3 + 1);
+ m = 4 * (l + 2) / 3 + 1;
+
+ if (line_break != SIZE_MAX)
+ m += m / line_break;
+
+ z = r = malloc(m);
if (!r)
return -ENOMEM;
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */
}
switch (l % 3) {
case 2:
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */
+ maybe_line_break(&z, r, line_break);
*(z++) = '=';
break;
case 1:
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char(x[0] >> 2); /* 00XXXXXX */
+ maybe_line_break(&z, r, line_break);
*(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */
+ maybe_line_break(&z, r, line_break);
*(z++) = '=';
+ maybe_line_break(&z, r, line_break);
*(z++) = '=';
break;
@@ -604,6 +645,7 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
*z = 0;
*out = r;
+ assert(z >= r); /* Let static analyzers know that the answer is non-negative. */
return z - r;
}
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index 4ace5b7a99..5218f78665 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -33,7 +33,11 @@ int unbase64char(char c) _const_;
char *base32hexmem(const void *p, size_t l, bool padding);
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
-ssize_t base64mem(const void *p, size_t l, char **out);
+ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
+static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
+ return base64mem_full(p, l, SIZE_MAX, ret);
+}
+
int base64_append(char **prefix, int plen,
const void *p, size_t l,
int margin, int width);
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index 072fed4378..a8476184c2 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -30,6 +30,7 @@
#define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
+#define _warn_unused_result_ __attribute__((__warn_unused_result__))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
@@ -213,7 +214,7 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
-#define STRLEN(x) (sizeof(""x"") - 1)
+#define STRLEN(x) ((unsigned) sizeof(""x"") - 1)
/*
* container_of - cast a member of a structure out to the containing structure
@@ -341,10 +342,10 @@ static inline int __coverity_check_and_return__(int condition) {
* negative '-' prefix (hence works correctly on signed
* types). Includes space for the trailing NUL. */
#define DECIMAL_STR_MAX(type) \
- (2+(sizeof(type) <= 1 ? 3 : \
- sizeof(type) <= 2 ? 5 : \
- sizeof(type) <= 4 ? 10 : \
- sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+ (2U+(sizeof(type) <= 1 ? 3U : \
+ sizeof(type) <= 2 ? 5U : \
+ sizeof(type) <= 4 ? 10U : \
+ sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)])))
#define DECIMAL_STR_WIDTH(x) \
({ \
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index b79c885dfd..70062eba2a 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -2,7 +2,6 @@
#include <errno.h>
#include <inttypes.h>
-#include <linux/oom.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
@@ -731,7 +730,7 @@ int parse_oom_score_adjust(const char *s, int *ret) {
if (r < 0)
return r;
- if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX)
+ if (!oom_score_adjust_is_valid(v))
return -ERANGE;
*ret = v;
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index eb547e7467..4aebb6541e 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -466,8 +466,10 @@ char *path_startswith_full(const char *path, const char *prefix, bool accept_dot
int path_compare(const char *a, const char *b) {
int r;
- assert(a);
- assert(b);
+ /* Order NULL before non-NULL */
+ r = CMP(!!a, !!b);
+ if (r != 0)
+ return r;
/* A relative path and an absolute path must not compare as equal.
* Which one is sorted before the other does not really matter.
@@ -514,10 +516,6 @@ int path_compare(const char *a, const char *b) {
}
}
-bool path_equal(const char *a, const char *b) {
- return path_compare(a, b) == 0;
-}
-
bool path_equal_or_files_same(const char *a, const char *b, int flags) {
return path_equal(a, b) || files_same(a, b, flags) > 0;
}
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index c6d4668794..26e7362d1f 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -62,7 +62,11 @@ static inline char* path_startswith(const char *path, const char *prefix) {
return path_startswith_full(path, prefix, true);
}
int path_compare(const char *a, const char *b) _pure_;
-bool path_equal(const char *a, const char *b) _pure_;
+
+static inline bool path_equal(const char *a, const char *b) {
+ return path_compare(a, b) == 0;
+}
+
bool path_equal_or_files_same(const char *a, const char *b, int flags);
/* Compares only the last portion of the input paths, ie: the filenames */
bool path_equal_filename(const char *a, const char *b);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 20b2441468..ecb14306f8 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -643,20 +643,23 @@ int get_process_environ(pid_t pid, char **env) {
return 0;
}
-int get_process_ppid(pid_t pid, pid_t *_ppid) {
- int r;
+int get_process_ppid(pid_t pid, pid_t *ret) {
_cleanup_free_ char *line = NULL;
long unsigned ppid;
const char *p;
+ int r;
assert(pid >= 0);
- assert(_ppid);
if (pid == 0 || pid == getpid_cached()) {
- *_ppid = getppid();
+ if (ret)
+ *ret = getppid();
return 0;
}
+ if (pid == 1) /* PID 1 has no parent, shortcut this case */
+ return -EADDRNOTAVAIL;
+
p = procfs_file_alloca(pid, "stat");
r = read_one_line_file(p, &line);
if (r == -ENOENT)
@@ -664,9 +667,8 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
if (r < 0)
return r;
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
+ /* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
+ * value, so let's skip over it manually */
p = strrchr(line, ')');
if (!p)
@@ -680,10 +682,17 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
&ppid) != 1)
return -EIO;
- if ((long unsigned) (pid_t) ppid != ppid)
+ /* If ppid is zero the process has no parent. Which might be the case for PID 1 but also for
+ * processes originating in other namespaces that are inserted into a pidns. Return a recognizable
+ * error in this case. */
+ if (ppid == 0)
+ return -EADDRNOTAVAIL;
+
+ if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid)
return -ERANGE;
- *_ppid = (pid_t) ppid;
+ if (ret)
+ *ret = (pid_t) ppid;
return 0;
}
@@ -1028,30 +1037,6 @@ bool is_main_thread(void) {
return cached > 0;
}
-_noreturn_ void freeze(void) {
-
- log_close();
-
- /* Make sure nobody waits for us on a socket anymore */
- (void) close_all_fds(NULL, 0);
-
- sync();
-
- /* Let's not freeze right away, but keep reaping zombies. */
- for (;;) {
- int r;
- siginfo_t si = {};
-
- r = waitid(P_ALL, 0, &si, WEXITED);
- if (r < 0 && errno != EINTR)
- break;
- }
-
- /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
- for (;;)
- pause();
-}
-
bool oom_score_adjust_is_valid(int oa) {
return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
}
@@ -1262,7 +1247,7 @@ static void restore_sigsetp(sigset_t **ssp) {
int safe_fork_full(
const char *name,
- const int except_fds[],
+ int except_fds[],
size_t n_except_fds,
ForkFlags flags,
pid_t *ret_pid) {
@@ -1457,7 +1442,7 @@ int safe_fork_full(
int namespace_fork(
const char *outer_name,
const char *inner_name,
- const int except_fds[],
+ int except_fds[],
size_t n_except_fds,
ForkFlags flags,
int pidns_fd,
@@ -1473,7 +1458,8 @@ int namespace_fork(
* process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
* /proc/self/fd works correctly. */
- r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+ r = safe_fork_full(outer_name, except_fds, n_except_fds,
+ (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
if (r < 0)
return r;
if (r == 0) {
@@ -1508,86 +1494,10 @@ int namespace_fork(
return 1;
}
-int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
- bool stdout_is_tty, stderr_is_tty;
- size_t n, i;
- va_list ap;
- char **l;
- int r;
-
- assert(path);
-
- /* Spawns a temporary TTY agent, making sure it goes away when we go away */
-
- r = safe_fork_full(name,
- except,
- n_except,
- FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG,
- ret_pid);
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
-
- /* In the child: */
-
- stdout_is_tty = isatty(STDOUT_FILENO);
- stderr_is_tty = isatty(STDERR_FILENO);
-
- if (!stdout_is_tty || !stderr_is_tty) {
- int fd;
-
- /* Detach from stdout/stderr. and reopen
- * /dev/tty for them. This is important to
- * ensure that when systemctl is started via
- * popen() or a similar call that expects to
- * read EOF we actually do generate EOF and
- * not delay this indefinitely by because we
- * keep an unused copy of stdin around. */
- fd = open("/dev/tty", O_WRONLY);
- if (fd < 0) {
- log_error_errno(errno, "Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- safe_close_above_stdio(fd);
- }
-
- (void) rlimit_nofile_safe();
-
- /* Count arguments */
- va_start(ap, path);
- for (n = 0; va_arg(ap, char*); n++)
- ;
- va_end(ap);
-
- /* Allocate strv */
- l = newa(char*, n + 1);
-
- /* Fill in arguments */
- va_start(ap, path);
- for (i = 0; i <= n; i++)
- l[i] = va_arg(ap, char*);
- va_end(ap);
-
- execv(path, l);
- _exit(EXIT_FAILURE);
-}
-
int set_oom_score_adjust(int value) {
char t[DECIMAL_STR_MAX(int)];
- sprintf(t, "%i", value);
+ xsprintf(t, "%i", value);
return write_string_file("/proc/self/oom_score_adj", t,
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 0e064de85e..c953649502 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -82,8 +82,6 @@ int pid_from_same_root_fs(pid_t pid);
bool is_main_thread(void);
-_noreturn_ void freeze(void);
-
bool oom_score_adjust_is_valid(int oa);
#ifndef PERSONALITY_INVALID
@@ -168,15 +166,13 @@ typedef enum ForkFlags {
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
} ForkFlags;
-int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
+int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, 0, flags, ret_pid);
}
-int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
-
-int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_;
+int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int set_oom_score_adjust(int value);
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index 131fd3ba00..b06b5ce774 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -253,3 +253,38 @@ int signal_is_blocked(int sig) {
return r;
}
+
+int pop_pending_signal_internal(int sig, ...) {
+ sigset_t ss;
+ va_list ap;
+ int r;
+
+ if (sig < 0) /* Empty list? */
+ return -EINVAL;
+
+ if (sigemptyset(&ss) < 0)
+ return -errno;
+
+ /* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build
+ * parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
+ * this.) */
+ if (sig > 0 && sigaddset(&ss, sig) < 0)
+ return -errno;
+
+ /* Add all other signals */
+ va_start(ap, sig);
+ r = sigset_add_many_ap(&ss, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
+ if (r < 0) {
+ if (errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+
+ return r; /* Returns the signal popped */
+}
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h
index 37271d7a68..36372c19bd 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.h
+++ b/src/libnm-systemd-shared/src/basic/signal-util.h
@@ -62,3 +62,6 @@ static inline const char* signal_to_string_with_check(int n) {
}
int signal_is_blocked(int sig);
+
+int pop_pending_signal_internal(int sig, ...);
+#define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1)
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index f6e751a09f..1c353e86b0 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -748,6 +748,22 @@ static const char* const ip_tos_table[] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
+bool ifname_valid_char(char a) {
+ if ((unsigned char) a >= 127U)
+ return false;
+
+ if ((unsigned char) a <= 32U)
+ return false;
+
+ if (IN_SET(a,
+ ':', /* colons are used by the legacy "alias" interface logic */
+ '/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
+ '%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
+ return false;
+
+ return true;
+}
+
bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
bool numeric = true;
@@ -781,16 +797,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
return false;
for (const char *t = p; *t; t++) {
- if ((unsigned char) *t >= 127U)
- return false;
-
- if ((unsigned char) *t <= 32U)
- return false;
-
- if (IN_SET(*t,
- ':', /* colons are used by the legacy "alias" interface logic */
- '/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */
- '%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */
+ if (!ifname_valid_char(*t))
return false;
numeric = numeric && (*t >= '0' && *t <= '9');
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index e0b959f5da..f92e425fd6 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -139,6 +139,7 @@ typedef enum {
IFNAME_VALID_NUMERIC = 1 << 1,
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
} IfnameValidFlags;
+bool ifname_valid_char(char a);
bool ifname_valid_full(const char *p, IfnameValidFlags flags);
static inline bool ifname_valid(const char *p) {
return ifname_valid_full(p, 0);
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 26a6762b17..c3b175a192 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -429,19 +429,37 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
s = "left";
}
- if (d >= USEC_PER_YEAR)
- snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
- d / USEC_PER_YEAR,
- (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
- else if (d >= USEC_PER_MONTH)
- snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
- d / USEC_PER_MONTH,
- (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
- else if (d >= USEC_PER_WEEK)
- snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
- d / USEC_PER_WEEK,
- (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
- else if (d >= 2*USEC_PER_DAY)
+ if (d >= USEC_PER_YEAR) {
+ usec_t years = d / USEC_PER_YEAR;
+ usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
+
+ snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+ years,
+ years == 1 ? "year" : "years",
+ months,
+ months == 1 ? "month" : "months",
+ s);
+ } else if (d >= USEC_PER_MONTH) {
+ usec_t months = d / USEC_PER_MONTH;
+ usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
+
+ snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+ months,
+ months == 1 ? "month" : "months",
+ days,
+ days == 1 ? "day" : "days",
+ s);
+ } else if (d >= USEC_PER_WEEK) {
+ usec_t weeks = d / USEC_PER_WEEK;
+ usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
+
+ snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+ weeks,
+ weeks == 1 ? "week" : "weeks",
+ days,
+ days == 1 ? "day" : "days",
+ s);
+ } else if (d >= 2*USEC_PER_DAY)
snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
else if (d >= 25*USEC_PER_HOUR)
snprintf(buf, l, "1 day " USEC_FMT "h %s",
@@ -535,14 +553,12 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
/* Let's see if we should shows this in dot notation */
if (t < USEC_PER_MINUTE && b > 0) {
- usec_t cc;
- signed char j;
+ signed char j = 0;
- j = 0;
- for (cc = table[i].usec; cc > 1; cc /= 10)
+ for (usec_t cc = table[i].usec; cc > 1; cc /= 10)
j++;
- for (cc = accuracy; cc > 1; cc /= 10) {
+ for (usec_t cc = accuracy; cc > 1; cc /= 10) {
b /= 10;
j--;
}
@@ -1244,74 +1260,126 @@ int parse_nsec(const char *t, nsec_t *nsec) {
return 0;
}
-int get_timezones(char ***ret) {
+static int get_timezones_from_zone1970_tab(char ***ret) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **zones = NULL;
- size_t n_zones = 0;
int r;
assert(ret);
- zones = strv_new("UTC");
- if (!zones)
- return -ENOMEM;
+ f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
+ if (!f)
+ return -errno;
- n_zones = 1;
+ for (;;) {
+ _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
- f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
- if (f) {
- for (;;) {
- _cleanup_free_ char *line = NULL, *w = NULL;
- char *p;
- size_t k;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- r = read_line(f, LONG_LINE_MAX, &line);
- if (r < 0)
- return r;
- if (r == 0)
- break;
+ const char *p = line;
+
+ /* Line format is:
+ * 'country codes' 'coordinates' 'timezone' 'comments' */
+ r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
+ if (r < 0)
+ continue;
- p = strstrip(line);
+ /* Lines that start with # are comments. */
+ if (*cc == '#')
+ continue;
+
+ r = strv_extend(&zones, tz);
+ if (r < 0)
+ return r;
+ }
- if (isempty(p) || *p == '#')
- continue;
+ *ret = TAKE_PTR(zones);
+ return 0;
+}
- /* Skip over country code */
- p += strcspn(p, WHITESPACE);
- p += strspn(p, WHITESPACE);
+static int get_timezones_from_tzdata_zi(char ***ret) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **zones = NULL;
+ int r;
- /* Skip over coordinates */
- p += strcspn(p, WHITESPACE);
- p += strspn(p, WHITESPACE);
+ f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
+ if (!f)
+ return -errno;
- /* Found timezone name */
- k = strcspn(p, WHITESPACE);
- if (k <= 0)
- continue;
+ for (;;) {
+ _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
- w = strndup(p, k);
- if (!w)
- return -ENOMEM;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
- if (!GREEDY_REALLOC(zones, n_zones + 2))
- return -ENOMEM;
+ const char *p = line;
- zones[n_zones++] = TAKE_PTR(w);
- zones[n_zones] = NULL;
- }
+ /* The only lines we care about are Zone and Link lines.
+ * Zone line format is:
+ * 'Zone' 'timezone' ...
+ * Link line format is:
+ * 'Link' 'target' 'alias'
+ * See 'man zic' for more detail. */
+ r = extract_many_words(&p, NULL, 0, &type, &f1, &f2, NULL);
+ if (r < 0)
+ continue;
- strv_sort(zones);
- strv_uniq(zones);
+ char *tz;
+ if (IN_SET(*type, 'Z', 'z'))
+ /* Zone lines have timezone in field 1. */
+ tz = f1;
+ else if (IN_SET(*type, 'L', 'l'))
+ /* Link lines have timezone in field 2. */
+ tz = f2;
+ else
+ /* Not a line we care about. */
+ continue;
- } else if (errno != ENOENT)
- return -errno;
+ r = strv_extend(&zones, tz);
+ if (r < 0)
+ return r;
+ }
*ret = TAKE_PTR(zones);
+ return 0;
+}
+
+int get_timezones(char ***ret) {
+ _cleanup_strv_free_ char **zones = NULL;
+ int r;
+
+ assert(ret);
+
+ r = get_timezones_from_tzdata_zi(&zones);
+ if (r == -ENOENT) {
+ log_debug_errno(r, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
+ r = get_timezones_from_zone1970_tab(&zones);
+ if (r == -ENOENT)
+ log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
+ }
+ if (r < 0 && r != -ENOENT)
+ return r;
+ /* Always include UTC */
+ r = strv_extend(&zones, "UTC");
+ if (r < 0)
+ return -ENOMEM;
+
+ strv_sort(zones);
+ strv_uniq(zones);
+
+ *ret = TAKE_PTR(zones);
return 0;
}
-bool timezone_is_valid(const char *name, int log_level) {
+int verify_timezone(const char *name, int log_level) {
bool slash = false;
const char *p, *t;
_cleanup_close_ int fd = -1;
@@ -1319,26 +1387,26 @@ bool timezone_is_valid(const char *name, int log_level) {
int r;
if (isempty(name))
- return false;
+ return -EINVAL;
/* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
if (streq(name, "UTC"))
- return true;
+ return 0;
if (name[0] == '/')
- return false;
+ return -EINVAL;
for (p = name; *p; p++) {
if (!(*p >= '0' && *p <= '9') &&
!(*p >= 'a' && *p <= 'z') &&
!(*p >= 'A' && *p <= 'Z') &&
!IN_SET(*p, '-', '_', '+', '/'))
- return false;
+ return -EINVAL;
if (*p == '/') {
if (slash)
- return false;
+ return -EINVAL;
slash = true;
} else
@@ -1346,38 +1414,31 @@ bool timezone_is_valid(const char *name, int log_level) {
}
if (slash)
- return false;
+ return -EINVAL;
if (p - name >= PATH_MAX)
- return false;
+ return -ENAMETOOLONG;
t = strjoina("/usr/share/zoneinfo/", name);
fd = open(t, O_RDONLY|O_CLOEXEC);
- if (fd < 0) {
- log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
- return false;
- }
+ if (fd < 0)
+ return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
r = fd_verify_regular(fd);
- if (r < 0) {
- log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
- return false;
- }
+ if (r < 0)
+ return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
r = loop_read_exact(fd, buf, 4, false);
- if (r < 0) {
- log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
- return false;
- }
+ if (r < 0)
+ return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
/* Magic from tzfile(5) */
- if (memcmp(buf, "TZif", 4) != 0) {
- log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
- return false;
- }
+ if (memcmp(buf, "TZif", 4) != 0)
+ return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
+ "Timezone file '%s' has wrong magic bytes", t);
- return true;
+ return 0;
}
bool clock_boottime_supported(void) {
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index cfde189818..895af88299 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -111,20 +111,31 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
usec_t timespec_load(const struct timespec *ts) _pure_;
nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
-struct timespec *timespec_store(struct timespec *ts, usec_t u);
-struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n);
+struct timespec* timespec_store(struct timespec *ts, usec_t u);
+struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
usec_t timeval_load(const struct timeval *tv) _pure_;
-struct timeval *timeval_store(struct timeval *tv, usec_t u);
+struct timeval* timeval_store(struct timeval *tv, usec_t u);
-char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style);
-char *format_timestamp_relative(char *buf, size_t l, usec_t t);
-char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
+char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
+char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
+char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
-static inline char *format_timestamp(char *buf, size_t l, usec_t t) {
+_warn_unused_result_
+static inline char* format_timestamp(char *buf, size_t l, usec_t t) {
return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY);
}
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 ยง6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define FORMAT_TIMESTAMP(t) format_timestamp((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t)
+#define FORMAT_TIMESTAMP_RELATIVE(t) \
+ format_timestamp_relative((char[FORMAT_TIMESTAMP_RELATIVE_MAX]){}, FORMAT_TIMESTAMP_RELATIVE_MAX, t)
+#define FORMAT_TIMESPAN(t, accuracy) format_timespan((char[FORMAT_TIMESPAN_MAX]){}, FORMAT_TIMESPAN_MAX, t, accuracy)
+#define FORMAT_TIMESTAMP_STYLE(t, style) \
+ format_timestamp_style((char[FORMAT_TIMESTAMP_MAX]){}, FORMAT_TIMESTAMP_MAX, t, style)
+
int parse_timestamp(const char *t, usec_t *usec);
int parse_sec(const char *t, usec_t *usec);
@@ -134,7 +145,10 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit);
int parse_nsec(const char *t, nsec_t *nsec);
int get_timezones(char ***l);
-bool timezone_is_valid(const char *name, int log_level);
+int verify_timezone(const char *name, int log_level);
+static inline bool timezone_is_valid(const char *name, int log_level) {
+ return verify_timezone(name, log_level) >= 0;
+}
bool clock_boottime_supported(void);
bool clock_supported(clockid_t clock);
@@ -153,9 +167,8 @@ usec_t jiffies_to_usec(uint32_t jiffies);
bool in_utc_timezone(void);
static inline usec_t usec_add(usec_t a, usec_t b) {
-
- /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't
- * overflow. */
+ /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output,
+ * and doesn't overflow. */
if (a > USEC_INFINITY - b) /* overflow check */
return USEC_INFINITY;
@@ -164,7 +177,6 @@ static inline usec_t usec_add(usec_t a, usec_t b) {
}
static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) {
-
if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */
return USEC_INFINITY;
if (timestamp < delta)
@@ -181,14 +193,14 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
}
#if SIZEOF_TIME_T == 8
-/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
- * territory. However, since we want to stay away from this in all timezones we take one day off. */
-#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
+ /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
+ * year territory. However, since we want to stay away from this in all timezones we take one day off. */
+# define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
#elif SIZEOF_TIME_T == 4
/* With a 32bit time_t we can't go beyond 2038... */
-#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
+# define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
#else
-#error "Yuck, time_t is neither 4 nor 8 bytes wide?"
+# error "Yuck, time_t is neither 4 nor 8 bytes wide?"
#endif
int time_change_fd(void);