diff options
author | Thomas Haller <thaller@redhat.com> | 2021-03-29 19:11:27 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2021-03-30 14:20:06 +0200 |
commit | 7935c1104455f444890ac35caede2c14c8fba485 (patch) | |
tree | c147dd1c9233ceecbd4837f51794579ec9ffda65 | |
parent | bd7a21e09900914c9f1b59f5e27821450d0c7369 (diff) | |
download | NetworkManager-7935c1104455f444890ac35caede2c14c8fba485.tar.gz |
systemd: update code from upstream (2021-03-30)
This is a direct dump from systemd git.
======
SYSTEMD_DIR=../systemd
COMMIT=119063d2b149667a91e0e08e4bdf82a0eb6a7efd
(
cd "$SYSTEMD_DIR"
git checkout "$COMMIT"
git reset --hard
git clean -fdx
)
git ls-files -z :/src/core/systemd/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/core/systemd/$(dirname "$1")"
cp "$SYSTEMD_DIR/$1" "./src/core/systemd/$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-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"
84 files changed, 2287 insertions, 1961 deletions
diff --git a/src/core/systemd/src/libsystemd-network/dhcp-identifier.h b/src/core/systemd/src/libsystemd-network/dhcp-identifier.h index e9f2ea7e95..1d9a9c55ba 100644 --- a/src/core/systemd/src/libsystemd-network/dhcp-identifier.h +++ b/src/core/systemd/src/libsystemd-network/dhcp-identifier.h @@ -16,7 +16,7 @@ typedef enum DUIDType { DUID_TYPE_LL = 3, DUID_TYPE_UUID = 4, _DUID_TYPE_MAX, - _DUID_TYPE_INVALID = -1, + _DUID_TYPE_INVALID = -EINVAL, } DUIDType; /* RFC 3315 section 9.1: diff --git a/src/core/systemd/src/libsystemd-network/dhcp-option.c b/src/core/systemd/src/libsystemd-network/dhcp-option.c index 70753c68d8..faa075cbd7 100644 --- a/src/core/systemd/src/libsystemd-network/dhcp-option.c +++ b/src/core/systemd/src/libsystemd-network/dhcp-option.c @@ -38,11 +38,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, size_t total = 0; char **s; + if (strv_isempty((char **) optval)) + return -EINVAL; + STRV_FOREACH(s, (char **) optval) { size_t len = strlen(*s); - if (len > 255) - return -ENAMETOOLONG; + if (len > 255 || len == 0) + return -EINVAL; total += 1 + len; } @@ -51,14 +54,13 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, return -ENOBUFS; options[*offset] = code; - options[*offset + 1] = total; + options[*offset + 1] = total; *offset += 2; STRV_FOREACH(s, (char **) optval) { size_t len = strlen(*s); options[*offset] = len; - memcpy(&options[*offset + 1], *s, len); *offset += 1 + len; } @@ -78,11 +80,11 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, break; case SD_DHCP_OPTION_VENDOR_SPECIFIC: { - OrderedHashmap *s = (OrderedHashmap *) optval; + OrderedSet *s = (OrderedSet *) optval; struct sd_dhcp_option *p; size_t l = 0; - ORDERED_HASHMAP_FOREACH(p, s) + ORDERED_SET_FOREACH(p, s) l += p->length + 2; if (*offset + l + 2 > size) @@ -93,7 +95,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, *offset += 2; - ORDERED_HASHMAP_FOREACH(p, s) { + ORDERED_SET_FOREACH(p, s) { options[*offset] = p->option; options[*offset + 1] = p->length; memcpy(&options[*offset + 2], p->data, p->length); diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-internal.h b/src/core/systemd/src/libsystemd-network/dhcp6-internal.h index 24d8a314a4..681c462315 100644 --- a/src/core/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/core/systemd/src/libsystemd-network/dhcp6-internal.h @@ -97,10 +97,10 @@ typedef struct DHCP6IA DHCP6IA; int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix); +int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class); -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class); +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); diff --git a/src/core/systemd/src/libsystemd-network/dhcp6-option.c b/src/core/systemd/src/libsystemd-network/dhcp6-option.c index e2bf4f7e36..91162d636b 100644 --- a/src/core/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/core/systemd/src/libsystemd-network/dhcp6-option.c @@ -41,14 +41,15 @@ typedef struct DHCP6PDPrefixOption { #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) -static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, - size_t optlen) { - DHCP6Option *option = (DHCP6Option*) *buf; +static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { + DHCP6Option *option; assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); + option = (DHCP6Option*) *buf; + if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; @@ -112,10 +113,13 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash } int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { - uint16_t len; - uint8_t *ia_hdr; - size_t iaid_offset, ia_buflen, ia_addrlen = 0; + size_t ia_buflen, ia_addrlen = 0; + struct ia_na ia_na; + struct ia_ta ia_ta; DHCP6Address *addr; + uint8_t *ia_hdr; + uint16_t len; + void *p; int r; assert_return(buf, -EINVAL); @@ -123,15 +127,23 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { assert_return(buflen, -EINVAL); assert_return(ia, -EINVAL); + /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */ + switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: len = DHCP6_OPTION_IA_NA_LEN; - iaid_offset = offsetof(DHCP6IA, ia_na); + ia_na = (struct ia_na) { + .id = ia->ia_na.id, + }; + p = &ia_na; break; case SD_DHCP6_OPTION_IA_TA: len = DHCP6_OPTION_IA_TA_LEN; - iaid_offset = offsetof(DHCP6IA, ia_ta); + ia_ta = (struct ia_ta) { + .id = ia->ia_ta.id, + }; + p = &ia_ta; break; default: @@ -147,30 +159,113 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); - memcpy(*buf, (char*) ia + iaid_offset, len); + memcpy(*buf, p, len); *buf += len; *buflen -= len; LIST_FOREACH(addresses, addr, ia->addresses) { - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, - sizeof(addr->iaaddr)); + struct iaaddr a = { + .address = addr->iaaddr.address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); if (r < 0) return r; - memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr)); + memcpy(*buf, &a, sizeof(struct iaaddr)); - *buf += sizeof(addr->iaaddr); - *buflen -= sizeof(addr->iaaddr); + *buf += sizeof(struct iaaddr); + *buflen -= sizeof(struct iaaddr); - ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr); + ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr); } - r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); + return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); +} + +static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) { + struct iapdprefix p; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(prefix); + + if (prefix->iapdprefix.prefixlen == 0) + return -EINVAL; + + /* Do not append T1 and T2. */ + + p = (struct iapdprefix) { + .prefixlen = prefix->iapdprefix.prefixlen, + .address = prefix->iapdprefix.address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); if (r < 0) return r; - return 0; + memcpy(*buf, &p, sizeof(struct iapdprefix)); + + *buf += sizeof(struct iapdprefix); + *buflen -= sizeof(struct iapdprefix); + + return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); +} + +int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) { + struct ia_pd ia_pd; + size_t len, pd_buflen; + uint8_t *pd_hdr; + int r; + + assert_return(buf, -EINVAL); + assert_return(*buf, -EINVAL); + assert_return(buflen, -EINVAL); + assert_return(pd, -EINVAL); + assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); + + /* Do not set T1 and T2. */ + ia_pd = (struct ia_pd) { + .id = pd->ia_pd.id, + }; + len = sizeof(struct ia_pd); + + if (*buflen < offsetof(DHCP6Option, data) + len) + return -ENOBUFS; + + pd_hdr = *buf; + pd_buflen = *buflen; + + /* The header will be written at the end of this function. */ + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); + + memcpy(*buf, &ia_pd, len); + + *buf += sizeof(struct ia_pd); + *buflen -= sizeof(struct ia_pd); + + DHCP6Address *prefix; + LIST_FOREACH(addresses, prefix, pd->addresses) { + r = option_append_pd_prefix(buf, buflen, prefix); + if (r < 0) + return r; + + len += r; + } + + if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) { + r = option_append_pd_prefix(buf, buflen, hint_pd_prefix); + if (r < 0) + return r; + + len += r; + } + + return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len); } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { @@ -200,19 +295,22 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) { +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { _cleanup_free_ uint8_t *p = NULL; size_t total = 0, offset = 0; - char **s; + char * const *s; - assert_return(buf && *buf && buflen && user_class, -EINVAL); + assert(buf); + assert(*buf); + assert(buflen); + assert(!strv_isempty(user_class)); STRV_FOREACH(s, user_class) { size_t len = strlen(*s); uint8_t *q; - if (len > 0xffff) - return -ENAMETOOLONG; + if (len > 0xffff || len == 0) + return -EINVAL; q = realloc(p, total + len + 2); if (!q) return -ENOMEM; @@ -229,16 +327,16 @@ int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_cl return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p); } -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) { +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *vendor_class) { _cleanup_free_ uint8_t *p = NULL; uint32_t enterprise_identifier; size_t total, offset; - char **s; + char * const *s; assert(buf); assert(*buf); assert(buflen); - assert(vendor_class); + assert(!strv_isempty(vendor_class)); enterprise_identifier = htobe32(SYSTEMD_PEN); @@ -253,6 +351,9 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendo size_t len = strlen(*s); uint8_t *q; + if (len > UINT16_MAX || len == 0) + return -EINVAL; + q = realloc(p, total + len + 2); if (!q) return -ENOMEM; @@ -269,51 +370,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendo return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); } -int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) { - DHCP6Option *option = (DHCP6Option *)buf; - size_t i = sizeof(*option) + sizeof(pd->ia_pd); - DHCP6PDPrefixOption *prefix_opt; - DHCP6Address *prefix; - - assert_return(buf, -EINVAL); - assert_return(pd, -EINVAL); - assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); - - if (len < i) - return -ENOBUFS; - - option->code = htobe16(SD_DHCP6_OPTION_IA_PD); - - memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd)); - LIST_FOREACH(addresses, prefix, pd->addresses) { - if (len < i + sizeof(*prefix_opt)) - return -ENOBUFS; - - prefix_opt = (DHCP6PDPrefixOption *)&buf[i]; - prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX); - prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix)); - - memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, sizeof(struct iapdprefix)); - i += sizeof(*prefix_opt); - } - - if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) { - if (len < i + sizeof(*prefix_opt)) - return -ENOBUFS; - - prefix_opt = (DHCP6PDPrefixOption *)&buf[i]; - prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX); - prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix)); - - memcpy(&prefix_opt->iapdprefix, &hint_pd_prefix->iapdprefix, sizeof(struct iapdprefix)); - i += sizeof(*prefix_opt); - } - - option->len = htobe16(i - sizeof(*option)); - - return i; -} - static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { DHCP6Option *option = (DHCP6Option*) *buf; uint16_t len; @@ -369,8 +425,7 @@ int dhcp6_option_parse_status(DHCP6Option *option, size_t len) { return be16toh(statusopt->status); } -static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, - uint32_t *lifetime_valid) { +static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option; DHCP6Address *addr; uint32_t lt_valid, lt_pref; @@ -383,16 +438,22 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred); if (lt_valid == 0 || lt_pref > lt_valid) { - log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d", + log_dhcp6_client(client, + "Valid lifetime of an IA address is zero or " + "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, lt_pref, lt_valid); - - return 0; + return -EINVAL; } if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); - if (r != 0) - return r < 0 ? r: 0; + if (r < 0) + return r; + if (r > 0) { + log_dhcp6_client(client, "Non-zero status code '%s' for address is received", + dhcp6_message_status_to_string(r)); + return -EINVAL; + } } addr = new0(DHCP6Address, 1); @@ -404,13 +465,12 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, LIST_PREPEND(addresses, ia->addresses, addr); - *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); + *ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); return 0; } -static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, - uint32_t *lifetime_valid) { +static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option; DHCP6Address *prefix; uint32_t lt_valid, lt_pref; @@ -423,16 +483,22 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred); if (lt_valid == 0 || lt_pref > lt_valid) { - log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d", + log_dhcp6_client(client, + "Valid lifetieme of a PD prefix is zero or " + "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, lt_pref, lt_valid); - - return 0; + return -EINVAL; } if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) { r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option)); - if (r != 0) - return r < 0 ? r: 0; + if (r < 0) + return r; + if (r > 0) { + log_dhcp6_client(client, "Non-zero status code '%s' for PD prefix is received", + dhcp6_message_status_to_string(r)); + return -EINVAL; + } } prefix = new0(DHCP6Address, 1); @@ -444,7 +510,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, LIST_PREPEND(addresses, ia->addresses, prefix); - *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); + *ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); return 0; } @@ -476,8 +542,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat lt_t2 = be32toh(ia->ia_na.lifetime_t2); if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { - log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", - lt_t1, lt_t2); + log_dhcp6_client(client, "IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec", lt_t1, lt_t2); return -EINVAL; } @@ -495,8 +560,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat lt_t2 = be32toh(ia->ia_pd.lifetime_t2); if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { - log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", - lt_t1, lt_t2); + log_dhcp6_client(client, "IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec", lt_t1, lt_t2); return -EINVAL; } @@ -536,10 +600,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat } r = dhcp6_option_parse_address(option, ia, <_valid); - if (r < 0) + if (r < 0 && r != -EINVAL) return r; - - if (lt_valid < lt_min) + if (r >= 0 && lt_valid < lt_min) lt_min = lt_valid; break; @@ -552,10 +615,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat } r = dhcp6_option_parse_pdprefix(option, ia, <_valid); - if (r < 0) + if (r < 0 && r != -EINVAL) return r; - - if (lt_valid < lt_min) + if (r >= 0 && lt_valid < lt_min) lt_min = lt_valid; break; @@ -588,26 +650,26 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat switch(iatype) { case SD_DHCP6_OPTION_IA_NA: - if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) { + if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2 && lt_min != UINT32_MAX) { lt_t1 = lt_min / 2; lt_t2 = lt_min / 10 * 8; ia->ia_na.lifetime_t1 = htobe32(lt_t1); ia->ia_na.lifetime_t2 = htobe32(lt_t2); - log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero", + log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero", lt_t1, lt_t2); } break; case SD_DHCP6_OPTION_IA_PD: - if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) { + if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2 && lt_min != UINT32_MAX) { lt_t1 = lt_min / 2; lt_t2 = lt_min / 10 * 8; ia->ia_pd.lifetime_t1 = htobe32(lt_t1); ia->ia_pd.lifetime_t2 = htobe32(lt_t2); - log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero", + log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero", lt_t1, lt_t2); } diff --git a/src/core/systemd/src/libsystemd-network/lldp-internal.h b/src/core/systemd/src/libsystemd-network/lldp-internal.h index f23695f974..daedbb088f 100644 --- a/src/core/systemd/src/libsystemd-network/lldp-internal.h +++ b/src/core/systemd/src/libsystemd-network/lldp-internal.h @@ -35,5 +35,5 @@ struct sd_lldp { #define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) #define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) -const char* lldp_event_to_string(sd_lldp_event e) _const_; -sd_lldp_event lldp_event_from_string(const char *s) _pure_; +const char* lldp_event_to_string(sd_lldp_event_t e) _const_; +sd_lldp_event_t lldp_event_from_string(const char *s) _pure_; diff --git a/src/core/systemd/src/libsystemd-network/lldp-network.c b/src/core/systemd/src/libsystemd-network/lldp-network.c index 9616cb6250..43141b2d79 100644 --- a/src/core/systemd/src/libsystemd-network/lldp-network.c +++ b/src/core/systemd/src/libsystemd-network/lldp-network.c @@ -22,7 +22,7 @@ int lldp_network_bind_raw_socket(int ifindex) { BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */ + BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */ }; static const struct sock_fprog fprog = { diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c index 7604aafd6f..fb94fdc882 100644 --- a/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/core/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -274,7 +274,6 @@ int sd_dhcp_client_set_request_address( } int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) { - assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); assert_return(ifindex > 0, -EINVAL); @@ -348,13 +347,14 @@ int sd_dhcp_client_get_client_id( assert_return(data, -EINVAL); assert_return(data_len, -EINVAL); - *type = 0; - *data = NULL; - *data_len = 0; if (client->client_id_len) { *type = client->client_id.type; *data = client->client_id.raw.data; *data_len = client->client_id_len - sizeof(client->client_id.type); + } else { + *type = 0; + *data = NULL; + *data_len = 0; } return 0; @@ -573,22 +573,26 @@ int sd_dhcp_client_set_mud_url( int sd_dhcp_client_set_user_class( sd_dhcp_client *client, - const char* const* user_class) { + char * const *user_class) { + + char * const *p; + char **s = NULL; - _cleanup_strv_free_ char **s = NULL; - char **p; + assert_return(client, -EINVAL); + assert_return(!strv_isempty(user_class), -EINVAL); + + STRV_FOREACH(p, user_class) { + size_t n = strlen(*p); - STRV_FOREACH(p, (char **) user_class) - if (strlen(*p) > 255) - return -ENAMETOOLONG; + if (n > 255 || n == 0) + return -EINVAL; + } - s = strv_copy((char **) user_class); + s = strv_copy(user_class); if (!s) return -ENOMEM; - client->user_class = TAKE_PTR(s); - - return 0; + return strv_free_and_replace(client->user_class, s); } int sd_dhcp_client_set_client_port( @@ -625,11 +629,7 @@ int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) { assert_return(client, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops); - if (r < 0) - return r; - - r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); + r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp_option_hash_ops, UINT_TO_PTR(v->option), v); if (r < 0) return r; @@ -2229,7 +2229,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) { .mtu = DHCP_DEFAULT_MIN_SIZE, .port = DHCP_PORT_CLIENT, .anonymize = !!anonymize, - .max_attempts = (uint64_t) -1, + .max_attempts = UINT64_MAX, .ip_service_type = -1, }; /* NOTE: this could be moved to a function. */ diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c index 8a138ff4b6..6d88c88e6b 100644 --- a/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/core/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -19,6 +19,7 @@ #include "env-file.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" @@ -98,7 +99,7 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { int sd_dhcp_lease_get_servers( sd_dhcp_lease *lease, - sd_dhcp_lease_server_type what, + sd_dhcp_lease_server_type_t what, const struct in_addr **addr) { assert_return(lease, -EINVAL); @@ -280,7 +281,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { free(lease->hostname); free(lease->domainname); - for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) + for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) free(lease->servers[i].addr); free(lease->static_route); @@ -868,7 +869,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { } int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { - _cleanup_free_ char *temp_path = NULL; + _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; struct sd_dhcp_raw_option *option; struct in_addr address; @@ -888,7 +889,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = fopen_temporary(lease_file, &f, &temp_path); if (r < 0) - goto fail; + return r; (void) fchmod(fileno(f), 0644); @@ -991,10 +992,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_free_ char *client_id_hex = NULL; client_id_hex = hexmem(client_id, client_id_len); - if (!client_id_hex) { - r = -ENOMEM; - goto fail; - } + if (!client_id_hex) + return -ENOMEM; fprintf(f, "CLIENTID=%s\n", client_id_hex); } @@ -1003,10 +1002,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_free_ char *option_hex = NULL; option_hex = hexmem(data, data_len); - if (!option_hex) { - r = -ENOMEM; - goto fail; - } + if (!option_hex) + return -ENOMEM; fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex); } @@ -1016,29 +1013,23 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { xsprintf(key, "OPTION_%" PRIu8, option->tag); r = serialize_dhcp_option(f, key, option->data, option->length); if (r < 0) - goto fail; + return r; } r = fflush_and_check(f); if (r < 0) - goto fail; - - if (rename(temp_path, lease_file) < 0) { - r = -errno; - goto fail; - } + return r; - return 0; + r = conservative_rename(temp_path, lease_file); + if (r < 0) + return r; -fail: - if (temp_path) - (void) unlink(temp_path); + temp_path = mfree(temp_path); - return log_error_errno(r, "Failed to save lease data %s: %m", lease_file); + return 0; } int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { - _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_free_ char *address = NULL, @@ -1266,13 +1257,13 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (client_id_hex) { - r = unhexmem(client_id_hex, (size_t) -1, &lease->client_id, &lease->client_id_len); + r = unhexmem(client_id_hex, SIZE_MAX, &lease->client_id, &lease->client_id_len); if (r < 0) log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex); } if (vendor_specific_hex) { - r = unhexmem(vendor_specific_hex, (size_t) -1, &lease->vendor_specific, &lease->vendor_specific_len); + r = unhexmem(vendor_specific_hex, SIZE_MAX, &lease->vendor_specific, &lease->vendor_specific_len); if (r < 0) log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex); } @@ -1284,7 +1275,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (!options[i]) continue; - r = unhexmem(options[i], (size_t) -1, &data, &len); + r = unhexmem(options[i], SIZE_MAX, &data, &len); if (r < 0) { log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]); continue; diff --git a/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c index e068e0dc91..410bfda10e 100644 --- a/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/core/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -157,7 +157,6 @@ int sd_dhcp6_client_set_callback( } int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { - assert_return(client, -EINVAL); assert_return(ifindex > 0, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); @@ -172,7 +171,7 @@ int sd_dhcp6_client_set_local_address( assert_return(client, -EINVAL); assert_return(local_address, -EINVAL); - assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL); + assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); @@ -235,11 +234,7 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * assert_return(client, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops); - if (r < 0) - return r; - - r = ordered_hashmap_put(client->vendor_options, v, v); + r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v); if (r < 0) return r; @@ -351,6 +346,7 @@ int sd_dhcp6_client_duid_as_string( assert_return(client, -EINVAL); assert_return(client->duid_len > 0, -ENODATA); + assert_return(duid, -EINVAL); v = dhcp6_duid_type_to_string(be16toh(client->duid.type)); if (v) { @@ -454,7 +450,6 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) } int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) { - assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); assert_return(mudurl, -EINVAL); @@ -464,47 +459,48 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud return free_and_strdup(&client->mudurl, mudurl); } -int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) { - _cleanup_strv_free_ char **s = NULL; - char **p; +int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) { + char * const *p; + char **s; assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!strv_isempty(user_class), -EINVAL); - assert_return(user_class, -EINVAL); + STRV_FOREACH(p, user_class) { + size_t len = strlen(*p); - STRV_FOREACH(p, user_class) - if (strlen(*p) > UINT16_MAX) - return -ENAMETOOLONG; + if (len > UINT16_MAX || len == 0) + return -EINVAL; + } s = strv_copy(user_class); if (!s) return -ENOMEM; - client->user_class = TAKE_PTR(s); - - return 0; + return strv_free_and_replace(client->user_class, s); } -int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) { - _cleanup_strv_free_ char **s = NULL; - char **p; +int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) { + char * const *p; + char **s; assert_return(client, -EINVAL); assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); - assert_return(vendor_class, -EINVAL); + assert_return(!strv_isempty(vendor_class), -EINVAL); + + STRV_FOREACH(p, vendor_class) { + size_t len = strlen(*p); - STRV_FOREACH(p, vendor_class) - if (strlen(*p) > UINT8_MAX) - return -ENAMETOOLONG; + if (len > UINT16_MAX || len == 0) + return -EINVAL; + } s = strv_copy(vendor_class); if (!s) return -ENOMEM; - client->vendor_class = TAKE_PTR(s); - - return 0; + return strv_free_and_replace(client->vendor_class, s); } int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) { @@ -567,11 +563,7 @@ int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { assert_return(client, -EINVAL); assert_return(v, -EINVAL); - r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops); - if (r < 0) - return r; - - r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v); + r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp6_option_hash_ops, UINT_TO_PTR(v->option), v); if (r < 0) return r; @@ -706,12 +698,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { } if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { - r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix); + r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix); if (r < 0) return r; - - opt += r; - optlen -= r; } break; @@ -770,12 +759,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { } if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); + r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); if (r < 0) return r; - - opt += r; - optlen -= r; } break; @@ -822,12 +808,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { } if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) { - r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL); + r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); if (r < 0) return r; - - opt += r; - optlen -= r; } break; @@ -1701,7 +1684,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); - assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL); + assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL); if (!IN_SET(client->state, DHCP6_STATE_STOPPED)) return -EBUSY; diff --git a/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c index 9426b65324..defd23d85a 100644 --- a/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c +++ b/src/core/systemd/src/libsystemd-network/sd-ipv4acd.c @@ -46,7 +46,7 @@ typedef enum IPv4ACDState { IPV4ACD_STATE_ANNOUNCING, IPV4ACD_STATE_RUNNING, _IPV4ACD_STATE_MAX, - _IPV4ACD_STATE_INVALID = -1 + _IPV4ACD_STATE_INVALID = -EINVAL, } IPv4ACDState; struct sd_ipv4acd { diff --git a/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c index 3af7d89bf0..a83c9b06de 100644 --- a/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c +++ b/src/core/systemd/src/libsystemd-network/sd-ipv4ll.c @@ -186,7 +186,7 @@ int sd_ipv4ll_is_running(sd_ipv4ll *ll) { static bool ipv4ll_address_is_valid(const struct in_addr *address) { assert(address); - if (!in_addr_is_link_local(AF_INET, (const union in_addr_union *) address)) + if (!in4_addr_is_link_local(address)) return false; return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U); diff --git a/src/core/systemd/src/libsystemd-network/sd-lldp.c b/src/core/systemd/src/libsystemd-network/sd-lldp.c index 8b666522cb..3c0285df4e 100644 --- a/src/core/systemd/src/libsystemd-network/sd-lldp.c +++ b/src/core/systemd/src/libsystemd-network/sd-lldp.c @@ -21,13 +21,13 @@ #define LLDP_DEFAULT_NEIGHBORS_MAX 128U static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { - [SD_LLDP_EVENT_ADDED] = "added", - [SD_LLDP_EVENT_REMOVED] = "removed", + [SD_LLDP_EVENT_ADDED] = "added", + [SD_LLDP_EVENT_REMOVED] = "removed", [SD_LLDP_EVENT_UPDATED] = "updated", [SD_LLDP_EVENT_REFRESHED] = "refreshed", }; -DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event); +DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event_t); static void lldp_flush_neighbors(sd_lldp *lldp) { assert(lldp); @@ -35,7 +35,7 @@ static void lldp_flush_neighbors(sd_lldp *lldp) { hashmap_clear(lldp->neighbor_by_id); } -static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { +static void lldp_callback(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n) { assert(lldp); assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); @@ -373,7 +373,7 @@ _public_ int sd_lldp_new(sd_lldp **ret) { .n_ref = 1, .fd = -1, .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, - .capability_mask = (uint16_t) -1, + .capability_mask = UINT16_MAX, }; lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); diff --git a/src/core/systemd/src/libsystemd/sd-event/event-source.h b/src/core/systemd/src/libsystemd/sd-event/event-source.h index f0d2a1b9e6..d2dc21470e 100644 --- a/src/core/systemd/src/libsystemd/sd-event/event-source.h +++ b/src/core/systemd/src/libsystemd/sd-event/event-source.h @@ -28,7 +28,7 @@ typedef enum EventSourceType { SOURCE_WATCHDOG, SOURCE_INOTIFY, _SOURCE_EVENT_SOURCE_TYPE_MAX, - _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 + _SOURCE_EVENT_SOURCE_TYPE_INVALID = -EINVAL, } EventSourceType; /* All objects we use in epoll events start with this value, so that @@ -40,7 +40,7 @@ typedef enum WakeupType { WAKEUP_SIGNAL_DATA, WAKEUP_INOTIFY_DATA, _WAKEUP_TYPE_MAX, - _WAKEUP_TYPE_INVALID = -1, + _WAKEUP_TYPE_INVALID = -EINVAL, } WakeupType; struct inode_data; @@ -56,7 +56,7 @@ struct sd_event_source { char *description; - EventSourceType type:5; + EventSourceType type; signed int enabled:3; bool pending:1; bool dispatching:1; diff --git a/src/core/systemd/src/libsystemd/sd-event/sd-event.c b/src/core/systemd/src/libsystemd/sd-event/sd-event.c index 3f1a6776fe..b76b0623fe 100644 --- a/src/core/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/core/systemd/src/libsystemd/sd-event/sd-event.c @@ -407,7 +407,7 @@ _public_ int sd_event_new(sd_event** ret) { e->epoll_fd = fd_move_above_stdio(e->epoll_fd); if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) { - log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 ... 2^63 us will be logged every 5s."); + log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 … 2^63 us will be logged every 5s."); e->profile_delays = true; } @@ -629,10 +629,6 @@ static int event_make_signal_data( return 0; } } else { - r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops); - if (r < 0) - return r; - d = new(struct signal_data, 1); if (!d) return -ENOMEM; @@ -643,7 +639,7 @@ static int event_make_signal_data( .priority = priority, }; - r = hashmap_put(e->signal_data, &d->priority, d); + r = hashmap_ensure_put(&e->signal_data, &uint64_hash_ops, &d->priority, d); if (r < 0) { free(d); return r; @@ -943,7 +939,7 @@ static void source_disconnect(sd_event_source *s) { sd_event_unref(event); } -static void source_free(sd_event_source *s) { +static sd_event_source* source_free(sd_event_source *s) { assert(s); source_disconnect(s); @@ -993,7 +989,7 @@ static void source_free(sd_event_source *s) { s->destroy_callback(s->userdata); free(s->description); - free(s); + return mfree(s); } DEFINE_TRIVIAL_CLEANUP_FUNC(sd_event_source*, source_free); @@ -1043,7 +1039,7 @@ static int source_set_pending(sd_event_source *s, bool b) { } } - return 0; + return 1; } static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) { @@ -1240,7 +1236,7 @@ _public_ int sd_event_add_time( assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); - assert_return(accuracy != (uint64_t) -1, -EINVAL); + assert_return(accuracy != UINT64_MAX, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1727,10 +1723,6 @@ static int event_make_inotify_data( fd = fd_move_above_stdio(fd); - r = hashmap_ensure_allocated(&e->inotify_data, &uint64_hash_ops); - if (r < 0) - return r; - d = new(struct inotify_data, 1); if (!d) return -ENOMEM; @@ -1741,7 +1733,7 @@ static int event_make_inotify_data( .priority = priority, }; - r = hashmap_put(e->inotify_data, &d->priority, d); + r = hashmap_ensure_put(&e->inotify_data, &uint64_hash_ops, &d->priority, d); if (r < 0) { d->fd = safe_close(d->fd); free(d); @@ -2602,10 +2594,11 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec if (r < 0) return r; - if (usec >= USEC_INFINITY - t) + usec = usec_add(t, usec); + if (usec == USEC_INFINITY) return -EOVERFLOW; - return sd_event_source_set_time(s, t + usec); + return sd_event_source_set_time(s, usec); } _public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) { @@ -2622,7 +2615,7 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec int r; assert_return(s, -EINVAL); - assert_return(usec != (uint64_t) -1, -EINVAL); + assert_return(usec != UINT64_MAX, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -3123,11 +3116,19 @@ static int process_timer( return 0; } -static int process_child(sd_event *e) { +static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) { + int64_t min_priority = threshold; + bool something_new = false; sd_event_source *s; int r; assert(e); + assert(ret_min_priority); + + if (!e->need_process_child) { + *ret_min_priority = min_priority; + return 0; + } e->need_process_child = false; @@ -3152,6 +3153,9 @@ static int process_child(sd_event *e) { HASHMAP_FOREACH(s, e->child_sources) { assert(s->type == SOURCE_CHILD); + if (s->priority > threshold) + continue; + if (s->pending) continue; @@ -3188,10 +3192,15 @@ static int process_child(sd_event *e) { r = source_set_pending(s, true); if (r < 0) return r; + if (r > 0) { + something_new = true; + min_priority = MIN(min_priority, s->priority); + } } } - return 0; + *ret_min_priority = min_priority; + return something_new; } static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) { @@ -3221,13 +3230,13 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) { return source_set_pending(s, true); } -static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { - bool read_one = false; +static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, int64_t *min_priority) { int r; assert(e); assert(d); assert_return(events == EPOLLIN, -EIO); + assert(min_priority); /* If there's a signal queued on this priority and SIGCHLD is on this priority too, then make sure to recheck the @@ -3253,7 +3262,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { n = read(d->fd, &si, sizeof(si)); if (n < 0) { if (IN_SET(errno, EAGAIN, EINTR)) - return read_one; + return 0; return -errno; } @@ -3263,8 +3272,6 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { assert(SIGNAL_VALID(si.ssi_signo)); - read_one = true; - if (e->signal_sources) s = e->signal_sources[si.ssi_signo]; if (!s) @@ -3278,12 +3285,16 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { r = source_set_pending(s, true); if (r < 0) return r; + if (r > 0 && *min_priority >= s->priority) { + *min_priority = s->priority; + return 1; /* an event source with smaller priority is queued. */ + } - return 1; + return 0; } } -static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents) { +static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t revents, int64_t threshold) { ssize_t n; assert(e); @@ -3299,6 +3310,9 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t if (d->buffer_filled > 0) return 0; + if (d->priority > threshold) + return 0; + n = read(d->fd, &d->buffer, sizeof(d->buffer)); if (n < 0) { if (IN_SET(errno, EAGAIN, EINTR)) @@ -3788,44 +3802,103 @@ pending: return r; } -_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { - size_t event_queue_max; - int r, m, i; +static int epoll_wait_usec( + int fd, + struct epoll_event *events, + int maxevents, + usec_t timeout) { - assert_return(e, -EINVAL); - assert_return(e = event_resolve(e), -ENOPKG); - assert_return(!event_pid_changed(e), -ECHILD); - assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); - assert_return(e->state == SD_EVENT_ARMED, -EBUSY); + int r, msec; +#if 0 + static bool epoll_pwait2_absent = false; - if (e->exit_requested) { - e->state = SD_EVENT_PENDING; - return 1; + /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. + * + * FIXME: this is temporarily disabled until epoll_pwait2() becomes more widely available. + * See https://github.com/systemd/systemd/pull/18973 and + * https://github.com/systemd/systemd/issues/19052. */ + + if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { + struct timespec ts; + + r = epoll_pwait2(fd, + events, + maxevents, + timespec_store(&ts, timeout), + NULL); + if (r >= 0) + return r; + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) + return -errno; /* Only fallback to old epoll_wait() if the syscall is masked or not + * supported. */ + + epoll_pwait2_absent = true; } +#endif - event_queue_max = MAX(e->n_sources, 1u); - if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, event_queue_max)) + if (timeout == USEC_INFINITY) + msec = -1; + else { + usec_t k; + + k = DIV_ROUND_UP(timeout, USEC_PER_MSEC); + if (k >= INT_MAX) + msec = INT_MAX; /* Saturate */ + else + msec = (int) k; + } + + r = epoll_wait(fd, + events, + maxevents, + msec); + if (r < 0) + return -errno; + + return r; +} + +static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) { + int64_t min_priority = threshold; + bool something_new = false; + size_t n_event_queue, m; + int r; + + assert(e); + assert(ret_min_priority); + + n_event_queue = MAX(e->n_sources, 1u); + if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, n_event_queue)) return -ENOMEM; /* If we still have inotify data buffered, then query the other fds, but don't wait on it */ if (e->inotify_data_buffered) timeout = 0; - m = epoll_wait(e->epoll_fd, e->event_queue, event_queue_max, - timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC)); - if (m < 0) { - if (errno == EINTR) { - e->state = SD_EVENT_PENDING; - return 1; - } + for (;;) { + r = epoll_wait_usec(e->epoll_fd, e->event_queue, e->event_queue_allocated, timeout); + if (r < 0) + return r; - r = -errno; - goto finish; + m = (size_t) r; + + if (m < e->event_queue_allocated) + break; + + if (e->event_queue_allocated >= n_event_queue * 10) + break; + + if (!GREEDY_REALLOC(e->event_queue, e->event_queue_allocated, e->event_queue_allocated + n_event_queue)) + return -ENOMEM; + + timeout = 0; } - triple_timestamp_get(&e->timestamp); + /* Set timestamp only when this is called first time. */ + if (threshold == INT64_MAX) + triple_timestamp_get(&e->timestamp); - for (i = 0; i < m; i++) { + for (size_t i = 0; i < m; i++) { if (e->event_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) r = flush_timer(e, e->watchdog_fd, e->event_queue[i].events, NULL); @@ -3839,6 +3912,11 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { assert(s); + if (s->priority > threshold) + continue; + + min_priority = MIN(min_priority, s->priority); + switch (s->type) { case SOURCE_IO: @@ -3866,11 +3944,11 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { } case WAKEUP_SIGNAL_DATA: - r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events); + r = process_signal(e, e->event_queue[i].data.ptr, e->event_queue[i].events, &min_priority); break; case WAKEUP_INOTIFY_DATA: - r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events); + r = event_inotify_data_read(e, e->event_queue[i].data.ptr, e->event_queue[i].events, threshold); break; default: @@ -3878,7 +3956,63 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { } } if (r < 0) + return r; + if (r > 0) + something_new = true; + } + + *ret_min_priority = min_priority; + return something_new; +} + +_public_ int sd_event_wait(sd_event *e, uint64_t timeout) { + int r; + + assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); + assert_return(!event_pid_changed(e), -ECHILD); + assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); + assert_return(e->state == SD_EVENT_ARMED, -EBUSY); + + if (e->exit_requested) { + e->state = SD_EVENT_PENDING; + return 1; + } + + for (int64_t threshold = INT64_MAX; ; threshold--) { + int64_t epoll_min_priority, child_min_priority; + + /* There may be a possibility that new epoll (especially IO) and child events are + * triggered just after process_epoll() call but before process_child(), and the new IO + * events may have higher priority than the child events. To salvage these events, + * let's call epoll_wait() again, but accepts only events with higher priority than the + * previous. See issue https://github.com/systemd/systemd/issues/18190 and comments + * https://github.com/systemd/systemd/pull/18750#issuecomment-785801085 + * https://github.com/systemd/systemd/pull/18922#issuecomment-792825226 */ + + r = process_epoll(e, timeout, threshold, &epoll_min_priority); + if (r == -EINTR) { + e->state = SD_EVENT_PENDING; + return 1; + } + if (r < 0) goto finish; + if (r == 0 && threshold < INT64_MAX) + /* No new epoll event. */ + break; + + r = process_child(e, threshold, &child_min_priority); + if (r < 0) + goto finish; + if (r == 0) + /* No new child event. */ + break; + + threshold = MIN(epoll_min_priority, child_min_priority); + if (threshold == INT64_MIN) + break; + + timeout = 0; } r = process_watchdog(e); @@ -3905,19 +4039,12 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { if (r < 0) goto finish; - if (e->need_process_child) { - r = process_child(e); - if (r < 0) - goto finish; - } - r = process_inotify(e); if (r < 0) goto finish; if (event_next_pending(e)) { e->state = SD_EVENT_PENDING; - return 1; } @@ -4029,7 +4156,7 @@ _public_ int sd_event_loop(sd_event *e) { _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL; while (e->state != SD_EVENT_FINISHED) { - r = sd_event_run(e, (uint64_t) -1); + r = sd_event_run(e, UINT64_MAX); if (r < 0) return r; } diff --git a/src/core/systemd/src/systemd/_sd-common.h b/src/core/systemd/src/systemd/_sd-common.h index e3de2ae562..e121429640 100644 --- a/src/core/systemd/src/systemd/_sd-common.h +++ b/src/core/systemd/src/systemd/_sd-common.h @@ -99,4 +99,10 @@ typedef void (*_sd_destroy_t)(void *userdata); } \ struct _sd_useless_struct_to_allow_trailing_semicolon_ +/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can + * freely extend them later on, without breaking compatibility. */ +#define _SD_ENUM_FORCE_S64(id) \ + _SD_##id##_INT64_MIN = INT64_MIN, \ + _SD_##id##_INT64_MAX = INT64_MAX + #endif diff --git a/src/core/systemd/src/systemd/sd-dhcp-client.h b/src/core/systemd/src/systemd/sd-dhcp-client.h index c35328a9a6..822286919e 100644 --- a/src/core/systemd/src/systemd/sd-dhcp-client.h +++ b/src/core/systemd/src/systemd/sd-dhcp-client.h @@ -181,7 +181,7 @@ int sd_dhcp_client_set_mud_url( const char *mudurl); int sd_dhcp_client_set_user_class( sd_dhcp_client *client, - const char* const *user_class); + char * const *user_class); int sd_dhcp_client_get_lease( sd_dhcp_client *client, sd_dhcp_lease **ret); diff --git a/src/core/systemd/src/systemd/sd-dhcp-lease.h b/src/core/systemd/src/systemd/sd-dhcp-lease.h index c255a1f912..5abf9a406c 100644 --- a/src/core/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/core/systemd/src/systemd/sd-dhcp-lease.h @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <inttypes.h> #include <net/ethernet.h> #include <netinet/in.h> @@ -33,7 +34,7 @@ typedef struct sd_dhcp_route sd_dhcp_route; sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease); sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease); -typedef enum sd_dhcp_lease_server_type { +typedef enum sd_dhcp_lease_server_type_t { SD_DHCP_LEASE_DNS, SD_DHCP_LEASE_NTP, SD_DHCP_LEASE_SIP, @@ -41,8 +42,9 @@ typedef enum sd_dhcp_lease_server_type { SD_DHCP_LEASE_SMTP, SD_DHCP_LEASE_LPR, _SD_DHCP_LEASE_SERVER_TYPE_MAX, - _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1, -} sd_dhcp_lease_server_type; + _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(DHCP_LEASE_SERVER_TYPE), +} sd_dhcp_lease_server_type_t; int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); @@ -53,7 +55,7 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr); +int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type_t what, const struct in_addr **addr); int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr); diff --git a/src/core/systemd/src/systemd/sd-dhcp6-client.h b/src/core/systemd/src/systemd/sd-dhcp6-client.h index 75ee27d68b..84e3170130 100644 --- a/src/core/systemd/src/systemd/sd-dhcp6-client.h +++ b/src/core/systemd/src/systemd/sd-dhcp6-client.h @@ -133,10 +133,10 @@ int sd_dhcp6_client_set_request_mud_url( const char *mudurl); int sd_dhcp6_client_set_request_user_class( sd_dhcp6_client *client, - char** user_class); + char * const *user_class); int sd_dhcp6_client_set_request_vendor_class( sd_dhcp6_client *client, - char** vendor_class); + char * const *vendor_class); int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, diff --git a/src/core/systemd/src/systemd/sd-lldp.h b/src/core/systemd/src/systemd/sd-lldp.h index f551f6b4fa..e48e29fbc0 100644 --- a/src/core/systemd/src/systemd/sd-lldp.h +++ b/src/core/systemd/src/systemd/sd-lldp.h @@ -17,6 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <inttypes.h> #include <net/ethernet.h> #include <sys/types.h> @@ -80,7 +81,7 @@ enum { SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, }; -#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1) +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL UINT16_MAX #define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ ((uint16_t) \ @@ -121,16 +122,17 @@ enum { typedef struct sd_lldp sd_lldp; typedef struct sd_lldp_neighbor sd_lldp_neighbor; -typedef enum sd_lldp_event { +typedef enum sd_lldp_event_t { SD_LLDP_EVENT_ADDED, SD_LLDP_EVENT_REMOVED, SD_LLDP_EVENT_UPDATED, SD_LLDP_EVENT_REFRESHED, _SD_LLDP_EVENT_MAX, - _SD_LLDP_EVENT_INVALID = -1, -} sd_lldp_event; + _SD_LLDP_EVENT_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(LLDP_EVENT), +} sd_lldp_event_t; -typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); +typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata); int sd_lldp_new(sd_lldp **ret); sd_lldp* sd_lldp_ref(sd_lldp *lldp); diff --git a/src/core/systemd/src/systemd/sd-ndisc.h b/src/core/systemd/src/systemd/sd-ndisc.h index c0e37899a7..49b127c018 100644 --- a/src/core/systemd/src/systemd/sd-ndisc.h +++ b/src/core/systemd/src/systemd/sd-ndisc.h @@ -19,6 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <inttypes.h> #include <net/ethernet.h> #include <netinet/in.h> @@ -54,14 +55,15 @@ enum { typedef struct sd_ndisc sd_ndisc; typedef struct sd_ndisc_router sd_ndisc_router; -typedef enum sd_ndisc_event { +typedef enum sd_ndisc_event_t { SD_NDISC_EVENT_TIMEOUT, SD_NDISC_EVENT_ROUTER, _SD_NDISC_EVENT_MAX, - _SD_NDISC_EVENT_INVALID = -1, -} sd_ndisc_event; + _SD_NDISC_EVENT_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(NDISC_EVENT), +} sd_ndisc_event_t; -typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata); +typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata); int sd_ndisc_new(sd_ndisc **ret); sd_ndisc *sd_ndisc_ref(sd_ndisc *nd); diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index f3e192ddaf..698a6583c5 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -80,7 +80,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s }) static inline void freep(void *p) { - free(*(void**) p); + *(void**)p = mfree(*(void**) p); } #define _cleanup_free_ _cleanup_(freep) @@ -158,15 +158,6 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); (void*)memset(_new_, 0, _xsize_); \ }) -/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time - * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ -#define TAKE_PTR(ptr) \ - ({ \ - typeof(ptr) _ptr_ = (ptr); \ - (ptr) = NULL; \ - _ptr_; \ - }) - #if HAS_FEATURE_MEMORY_SANITIZER # define msan_unpoison(r, s) __msan_unpoison(r, s) #else diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index bdc0d0d086..f79e384147 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -32,7 +32,7 @@ typedef enum CGroupController { CGROUP_CONTROLLER_BPF_DEVICES, _CGROUP_CONTROLLER_MAX, - _CGROUP_CONTROLLER_INVALID = -1, + _CGROUP_CONTROLLER_INVALID = -EINVAL, } CGroupController; #define CGROUP_CONTROLLER_TO_MASK(c) (1U << (c)) @@ -75,13 +75,13 @@ CGroupMask get_cpu_accounting_mask(void); bool cpu_accounting_is_cheap(void); /* Special values for all weight knobs on unified hierarchy */ -#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_WEIGHT_INVALID UINT64_MAX #define CGROUP_WEIGHT_MIN UINT64_C(1) #define CGROUP_WEIGHT_MAX UINT64_C(10000) #define CGROUP_WEIGHT_DEFAULT UINT64_C(100) #define CGROUP_LIMIT_MIN UINT64_C(0) -#define CGROUP_LIMIT_MAX ((uint64_t) -1) +#define CGROUP_LIMIT_MAX UINT64_MAX static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { return @@ -97,7 +97,7 @@ typedef enum CGroupIOLimitType { CGROUP_IO_WIOPS_MAX, _CGROUP_IO_LIMIT_TYPE_MAX, - _CGROUP_IO_LIMIT_TYPE_INVALID = -1 + _CGROUP_IO_LIMIT_TYPE_INVALID = -EINVAL, } CGroupIOLimitType; extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; @@ -106,7 +106,7 @@ const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; /* Special values for the cpu.shares attribute */ -#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) +#define CGROUP_CPU_SHARES_INVALID UINT64_MAX #define CGROUP_CPU_SHARES_MIN UINT64_C(2) #define CGROUP_CPU_SHARES_MAX UINT64_C(262144) #define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024) @@ -118,7 +118,7 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { } /* Special values for the blkio.weight attribute */ -#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX #define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) #define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000) #define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500) @@ -212,10 +212,13 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret); int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid); +int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid); int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags); int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size); int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret); +/* Returns negative on error, and 0 or 1 on success for the bool value */ +int cg_get_xattr_bool(const char *controller, const char *path, const char *name); int cg_remove_xattr(const char *controller, const char *path, const char *name); int cg_install_release_agent(const char *controller, const char *agent); @@ -257,6 +260,7 @@ int cg_slice_to_path(const char *unit, char **ret); typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); int cg_mask_supported(CGroupMask *ret); +int cg_mask_supported_subtree(const char *root, CGroupMask *ret); int cg_mask_from_string(const char *s, CGroupMask *ret); int cg_mask_to_string(CGroupMask mask, char **ret); @@ -283,8 +287,19 @@ typedef enum ManagedOOMMode { MANAGED_OOM_AUTO, MANAGED_OOM_KILL, _MANAGED_OOM_MODE_MAX, - _MANAGED_OOM_MODE_INVALID = -1, + _MANAGED_OOM_MODE_INVALID = -EINVAL, } ManagedOOMMode; const char* managed_oom_mode_to_string(ManagedOOMMode m) _const_; ManagedOOMMode managed_oom_mode_from_string(const char *s) _pure_; + +typedef enum ManagedOOMPreference { + MANAGED_OOM_PREFERENCE_NONE = 0, + MANAGED_OOM_PREFERENCE_AVOID = 1, + MANAGED_OOM_PREFERENCE_OMIT = 2, + _MANAGED_OOM_PREFERENCE_MAX, + _MANAGED_OOM_PREFERENCE_INVALID = -EINVAL, +} ManagedOOMPreference; + +const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_; +ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_; diff --git a/src/libnm-systemd-shared/src/basic/dns-def.h b/src/libnm-systemd-shared/src/basic/dns-def.h new file mode 100644 index 0000000000..d70220bcc0 --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/dns-def.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */ +#define DNS_LABEL_MAX 63 + +/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */ +#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) + +/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */ +#define DNS_HOSTNAME_MAX 253 + +/* Maximum length of a full hostname, on the wire, including the final NUL byte */ +#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255 + +/* Maximum number of labels per valid hostname */ +#define DNS_N_LABELS_MAX 127 diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index 99c3e3f4a3..a300909360 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -20,7 +20,7 @@ static int parse_env_file_internal( void *userdata, int *n_pushed) { - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; + size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; unsigned line = 1; char *p; @@ -56,7 +56,7 @@ static int parse_env_file_internal( state = COMMENT; else if (!strchr(WHITESPACE, c)) { state = KEY; - last_key_whitespace = (size_t) -1; + last_key_whitespace = SIZE_MAX; if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) return -ENOMEM; @@ -72,11 +72,11 @@ static int parse_env_file_internal( n_key = 0; } else if (c == '=') { state = PRE_VALUE; - last_value_whitespace = (size_t) -1; + last_value_whitespace = SIZE_MAX; } else { if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) + last_key_whitespace = SIZE_MAX; + else if (last_key_whitespace == SIZE_MAX) last_key_whitespace = n_key; if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) @@ -97,7 +97,7 @@ static int parse_env_file_internal( value[n_value] = 0; /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) + if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; r = push(fname, line, key, value, userdata, n_pushed); @@ -136,11 +136,11 @@ static int parse_env_file_internal( value[n_value] = 0; /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) + if (last_value_whitespace != SIZE_MAX) value[last_value_whitespace] = 0; /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) + if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; r = push(fname, line, key, value, userdata, n_pushed); @@ -153,11 +153,11 @@ static int parse_env_file_internal( } else if (c == '\\') { state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; + last_value_whitespace = SIZE_MAX; } else { if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) + last_value_whitespace = SIZE_MAX; + else if (last_value_whitespace == SIZE_MAX) last_value_whitespace = n_value; if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) @@ -255,11 +255,11 @@ static int parse_env_file_internal( value[n_value] = 0; if (state == VALUE) - if (last_value_whitespace != (size_t) -1) + if (last_value_whitespace != SIZE_MAX) value[last_value_whitespace] = 0; /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) + if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; r = push(fname, line, key, value, userdata, n_pushed); @@ -385,11 +385,9 @@ static int load_env_file_push( if (!p) return -ENOMEM; - r = strv_env_replace(m, p); - if (r < 0) { - free(p); + r = strv_env_replace_consume(m, p); + if (r < 0) return r; - } if (n_pushed) (*n_pushed)++; diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index a84863ff22..c110a750a5 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -12,6 +12,9 @@ #include "extract-word.h" #include "macro.h" #include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "utf8.h" @@ -22,8 +25,6 @@ "_" static bool env_name_is_valid_n(const char *e, size_t n) { - const char *p; - if (!e) return false; @@ -41,7 +42,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) { if (n > (size_t) sysconf(_SC_ARG_MAX) - 2) return false; - for (p = e; p < e + n; p++) + for (const char *p = e; p < e + n; p++) if (!strchr(VALID_BASH_ENV_NAME_CHARS, *p)) return false; @@ -59,16 +60,13 @@ bool env_value_is_valid(const char *e) { if (!utf8_is_valid(e)) return false; - /* bash allows tabs and newlines in environment variables, and so - * should we */ - if (string_has_cc(e, "\t\n")) - return false; + /* Note that variable *values* may contain control characters, in particular NL, TAB, BS, DEL, ESC… + * When printing those variables with show-environment, we'll escape them. Make sure to print + * environment variables carefully! */ - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the shortest possible variable name of - * length 1, the equal sign and trailing NUL this hence leaves - * ARG_MAX-3 as longest possible variable value. */ + /* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment + * hence cannot be either. Discounting the shortest possible variable name of length 1, the equal + * sign and trailing NUL this hence leaves ARG_MAX-3 as longest possible variable value. */ if (strlen(e) > sc_arg_max() - 3) return false; @@ -88,10 +86,8 @@ bool env_assignment_is_valid(const char *e) { if (!env_value_is_valid(eq + 1)) return false; - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, hence the individual variable assignments - * cannot be either, but let's leave room for one trailing NUL - * byte. */ + /* POSIX says the overall size of the environment block cannot be > ARG_MAX, hence the individual + * variable assignments cannot be either, but let's leave room for one trailing NUL byte. */ if (strlen(e) > sc_arg_max() - 1) return false; @@ -189,14 +185,14 @@ static int env_append(char **r, char ***k, char **a) { char **strv_env_merge(size_t n_lists, ...) { _cleanup_strv_free_ char **ret = NULL; - size_t n = 0, i; + size_t n = 0; char **l, **k; va_list ap; /* Merges an arbitrary number of environment sets */ va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { + for (size_t i = 0; i < n_lists; i++) { l = va_arg(ap, char**); n += strv_length(l); } @@ -210,7 +206,7 @@ char **strv_env_merge(size_t n_lists, ...) { k = ret; va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { + for (size_t i = 0; i < n_lists; i++) { l = va_arg(ap, char**); if (env_append(ret, &k, l) < 0) { va_end(ap); @@ -276,10 +272,8 @@ char **strv_env_delete(char **x, size_t n_lists, ...) { return NULL; STRV_FOREACH(k, x) { - size_t v; - va_start(ap, n_lists); - for (v = 0; v < n_lists; v++) { + for (size_t v = 0; v < n_lists; v++) { char **l, **j; l = va_arg(ap, char**); @@ -310,7 +304,6 @@ char **strv_env_delete(char **x, size_t n_lists, ...) { } char **strv_env_unset(char **l, const char *p) { - char **f, **t; if (!l) @@ -371,20 +364,23 @@ char **strv_env_unset_many(char **l, ...) { return l; } -int strv_env_replace(char ***l, char *p) { +int strv_env_replace_consume(char ***l, char *p) { const char *t, *name; char **f; int r; assert(p); - /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits - * in-place. Does not copy p. p must be a valid key=value assignment. - */ + /* Replace first occurrence of the env var or add a new one in the string list. Drop other + * occurrences. Edits in-place. Does not copy p and CONSUMES p EVEN ON FAILURE. + * + * p must be a valid key=value assignment. */ t = strchr(p, '='); - if (!t) + if (!t) { + free(p); return -EINVAL; + } name = strndupa(p, t - p); @@ -396,39 +392,39 @@ int strv_env_replace(char ***l, char *p) { } /* We didn't find a match, we need to append p or create a new strv */ - r = strv_push(l, p); + r = strv_consume(l, p); if (r < 0) return r; return 1; } -char **strv_env_set(char **x, const char *p) { - _cleanup_strv_free_ char **ret = NULL; - size_t n, m; - char **k; +int strv_env_replace_strdup(char ***l, const char *assignment) { + /* Like strv_env_replace_consume(), but copies the argument. */ - /* Overrides the env var setting of p, returns a new copy */ + char *p = strdup(assignment); + if (!p) + return -ENOMEM; - n = strv_length(x); - m = n + 2; - if (m < n) /* overflow? */ - return NULL; + return strv_env_replace_consume(l, p); +} - ret = new(char*, m); - if (!ret) - return NULL; +int strv_env_assign(char ***l, const char *key, const char *value) { + if (!env_name_is_valid(key)) + return -EINVAL; - *ret = NULL; - k = ret; + /* NULL removes assignment, "" creates an empty assignment. */ - if (env_append(ret, &k, x) < 0) - return NULL; + if (!value) { + strv_env_unset(*l, key); + return 0; + } - if (env_append(ret, &k, STRV_MAKE(p)) < 0) - return NULL; + char *p = strjoin(key, "=", value); + if (!p) + return -ENOMEM; - return TAKE_PTR(ret); + return strv_env_replace_consume(l, p); } char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { @@ -460,6 +456,18 @@ char *strv_env_get(char **l, const char *name) { return strv_env_get_n(l, name, strlen(name), 0); } +char *strv_env_pairs_get(char **l, const char *name) { + char **key, **value, *result = NULL; + + assert(name); + + STRV_FOREACH_PAIR(key, value, l) + if (streq(*key, name)) + result = *value; + + return result; +} + char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { char **p, **q; int k = 0; @@ -759,3 +767,78 @@ int set_unset_env(const char *name, const char *value, bool overwrite) { return -errno; return 0; } + +int putenv_dup(const char *assignment, bool override) { + const char *e, *n; + + e = strchr(assignment, '='); + if (!e) + return -EINVAL; + + n = strndupa(assignment, e - assignment); + + /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */ + if (setenv(n, e + 1, override) < 0) + return -errno; + return 0; +} + +int setenv_systemd_exec_pid(bool update_only) { + char str[DECIMAL_STR_MAX(pid_t)]; + const char *e; + + /* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */ + + e = secure_getenv("SYSTEMD_EXEC_PID"); + if (!e && update_only) + return 0; + + if (streq_ptr(e, "*")) + return 0; + + xsprintf(str, PID_FMT, getpid_cached()); + + if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0) + return -errno; + + return 1; +} + +int getenv_path_list(const char *name, char ***ret_paths) { + _cleanup_strv_free_ char **l = NULL; + const char *e; + char **p; + int r; + + assert(name); + assert(ret_paths); + + e = secure_getenv(name); + if (!e) + return -ENXIO; + + r = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return log_debug_errno(r, "Failed to parse $%s: %m", name); + + STRV_FOREACH(p, l) { + if (!path_is_absolute(*p)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Path '%s' is not absolute, refusing.", *p); + + if (!path_is_normalized(*p)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Path '%s' is not normalized, refusing.", *p); + + if (path_equal(*p, "/")) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Path '%s' is the root fs, refusing.", *p); + } + + if (strv_isempty(l)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "No paths specified, refusing."); + + *ret_paths = TAKE_PTR(l); + return 1; +} diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h index 6684b3350f..1fbe7e4270 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.h +++ b/src/libnm-systemd-shared/src/basic/env-util.h @@ -42,16 +42,27 @@ bool strv_env_name_or_assignment_is_valid(char **l); char **strv_env_merge(size_t n_lists, ...); char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ -char **strv_env_set(char **x, const char *p); /* New copy ... */ char **strv_env_unset(char **l, const char *p); /* In place ... */ char **strv_env_unset_many(char **l, ...) _sentinel_; -int strv_env_replace(char ***l, char *p); /* In place ... */ +int strv_env_replace_consume(char ***l, char *p); /* In place ... */ +int strv_env_replace_strdup(char ***l, const char *assignment); +int strv_env_assign(char ***l, const char *key, const char *value); char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; char *strv_env_get(char **x, const char *n) _pure_; +char *strv_env_pairs_get(char **l, const char *name) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); /* Like setenv, but calls unsetenv if value == NULL. */ int set_unset_env(const char *name, const char *value, bool overwrite); + +/* Like putenv, but duplicates the memory like setenv. */ +int putenv_dup(const char *assignment, bool override); + +int setenv_systemd_exec_pid(bool update_only); + +/* Parses and does sanity checks on an environment variable containing + * PATH-like colon-separated absolute paths */ +int getenv_path_list(const char *name, char ***ret_paths); diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c index 31f3cda472..af785ecfa4 100644 --- a/src/libnm-systemd-shared/src/basic/escape.c +++ b/src/libnm-systemd-shared/src/basic/escape.c @@ -114,7 +114,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, * instead be copied directly. */ - if (length != (size_t) -1 && length < 1) + if (length != SIZE_MAX && length < 1) return -EINVAL; switch (p[0]) { @@ -159,7 +159,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, /* hexadecimal encoding */ int a, b; - if (length != (size_t) -1 && length < 3) + if (length != SIZE_MAX && length < 3) return -EINVAL; a = unhexchar(p[1]); @@ -187,7 +187,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, size_t i; uint32_t c; - if (length != (size_t) -1 && length < 5) + if (length != SIZE_MAX && length < 5) return -EINVAL; for (i = 0; i < 4; i++) { @@ -214,7 +214,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, size_t i; char32_t c; - if (length != (size_t) -1 && length < 9) + if (length != SIZE_MAX && length < 9) return -EINVAL; for (i = 0; i < 8; i++) { @@ -251,7 +251,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, int a, b, c; char32_t m; - if (length != (size_t) -1 && length < 3) + if (length != SIZE_MAX && length < 3) return -EINVAL; a = unoctchar(p[0]); diff --git a/src/libnm-systemd-shared/src/basic/extract-word.c b/src/libnm-systemd-shared/src/basic/extract-word.c index 76b3fe12e3..d1af11318a 100644 --- a/src/libnm-systemd-shared/src/basic/extract-word.c +++ b/src/libnm-systemd-shared/src/basic/extract-word.c @@ -20,11 +20,10 @@ int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { _cleanup_free_ char *s = NULL; size_t allocated = 0, sz = 0; - char c; - int r; - char quote = 0; /* 0 or ' or " */ bool backslash = false; /* whether we've just seen a backslash */ + char c; + int r; assert(p); assert(ret); @@ -70,14 +69,14 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra return -ENOMEM; if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && - (!quote || flags & EXTRACT_RELAX)) { + if ((flags & EXTRACT_UNESCAPE_RELAX) && + (quote == 0 || flags & EXTRACT_RELAX)) { /* If we find an unquoted trailing backslash and we're in - * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the + * EXTRACT_UNESCAPE_RELAX mode, keep it verbatim in the * output. * * Unbalanced quotes will only be allowed in EXTRACT_RELAX - * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. + * mode, EXTRACT_UNESCAPE_RELAX mode does not allow them. */ s[sz++] = '\\'; goto finish_force_terminate; @@ -92,7 +91,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra char32_t u; if ((flags & EXTRACT_CUNESCAPE) && - (r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false)) >= 0) { + (r = cunescape_one(*p, SIZE_MAX, &u, &eight_bit, false)) >= 0) { /* A valid escaped sequence */ assert(r >= 1); @@ -103,10 +102,10 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra else sz += utf8_encode_unichar(s + sz, u); } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) && - strchr(separators, **p)) - /* An escaped separator char */ + (strchr(separators, **p) || **p == '\\')) + /* An escaped separator char or the escape char itself */ s[sz++] = c; - else if (flags & EXTRACT_CUNESCAPE_RELAX) { + else if (flags & EXTRACT_UNESCAPE_RELAX) { s[sz++] = '\\'; s[sz++] = c; } else @@ -116,7 +115,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra backslash = false; - } else if (quote) { /* inside either single or double quotes */ + } else if (quote != 0) { /* inside either single or double quotes */ for (;; (*p)++, c = **p) { if (c == 0) { if (flags & EXTRACT_RELAX) @@ -197,7 +196,7 @@ int extract_first_word_and_warn( const char *rvalue) { /* Try to unquote it, if it fails, warn about it and try again - * but this time using EXTRACT_CUNESCAPE_RELAX to keep the + * but this time using EXTRACT_UNESCAPE_RELAX to keep the * backslashes verbatim in invalid escape sequences. */ const char *save; @@ -208,11 +207,11 @@ int extract_first_word_and_warn( if (r >= 0) return r; - if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) { + if (r == -EINVAL && !(flags & EXTRACT_UNESCAPE_RELAX)) { - /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ + /* Retry it with EXTRACT_UNESCAPE_RELAX. */ *p = save; - r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); + r = extract_first_word(p, ret, separators, flags|EXTRACT_UNESCAPE_RELAX); if (r >= 0) { /* It worked this time, hence it must have been an invalid escape sequence. */ log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret); diff --git a/src/libnm-systemd-shared/src/basic/extract-word.h b/src/libnm-systemd-shared/src/basic/extract-word.h index d1de32e580..0e9e77e93d 100644 --- a/src/libnm-systemd-shared/src/basic/extract-word.h +++ b/src/libnm-systemd-shared/src/basic/extract-word.h @@ -4,13 +4,15 @@ #include "macro.h" typedef enum ExtractFlags { - EXTRACT_RELAX = 1 << 0, - EXTRACT_CUNESCAPE = 1 << 1, - EXTRACT_CUNESCAPE_RELAX = 1 << 2, - EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, - EXTRACT_UNQUOTE = 1 << 4, - EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, - EXTRACT_RETAIN_ESCAPE = 1 << 6, + EXTRACT_RELAX = 1 << 0, /* Allow unbalanced quote and eat up trailing backslash. */ + 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 */ + + /* Note that if no flags are specified, escaped escape characters will be silently stripped. */ } ExtractFlags; int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index a03ba83e19..d63f012ad5 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -91,11 +91,9 @@ void safe_close_pair(int p[static 2]) { } void close_many(const int fds[], size_t n_fd) { - size_t i; - assert(fds || n_fd <= 0); - for (i = 0; i < n_fd; i++) + for (size_t i = 0; i < n_fd; i++) safe_close(fds[i]); } @@ -179,11 +177,9 @@ int fd_cloexec(int fd, bool cloexec) { } _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { - size_t i; - assert(n_fdset == 0 || fdset); - for (i = 0; i < n_fdset; i++) + for (size_t i = 0; i < n_fdset; i++) if (fdset[i] == fd) return true; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index 2162537b80..f05c2b5a15 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -41,8 +41,8 @@ static inline void fclosep(FILE **f) { safe_fclose(*f); } -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); -DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL); #define _cleanup_close_ _cleanup_(closep) #define _cleanup_fclose_ _cleanup_(fclosep) diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index f4708bc05f..a158ab080d 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -27,7 +27,8 @@ #include "string-util.h" #include "tmpfile-util.h" -#define READ_FULL_BYTES_MAX (4U*1024U*1024U) +/* The maximum size of the file we'll read in one go. */ +#define READ_FULL_BYTES_MAX (4U*1024U*1024U - 1) int fopen_unlocked(const char *path, const char *options, FILE **ret) { assert(ret); @@ -368,7 +369,6 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re struct stat st; size_t n, size; int n_retries; - char *p; assert(ret_contents); @@ -385,8 +385,12 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re if (fd < 0) return -errno; - /* Start size for files in /proc which usually report a file size of 0. */ - size = LINE_MAX / 2; + /* Start size for files in /proc/ which usually report a file size of 0. (Files in /sys/ report a + * file size of 4K, which is probably OK for sizing our initial buffer, and sysfs attributes can't be + * larger anyway.) + * + * It's one less than 4k, so that the malloc() below allocates exactly 4k. */ + size = 4095; /* Limit the number of attempts to read the number of bytes returned by fstat(). */ n_retries = 3; @@ -402,19 +406,27 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re return -EBADF; /* Be prepared for files from /proc which generally report a file size of 0. */ + assert_cc(READ_FULL_BYTES_MAX < SSIZE_MAX); if (st.st_size > 0) { + if (st.st_size > READ_FULL_BYTES_MAX) + return -E2BIG; + size = st.st_size; n_retries--; - } else - size = size * 2; - - if (size > READ_FULL_BYTES_MAX) - return -E2BIG; + } else { + /* Double the buffer size */ + if (size >= READ_FULL_BYTES_MAX) + return -E2BIG; + if (size > READ_FULL_BYTES_MAX / 2 - 1) + size = READ_FULL_BYTES_MAX; /* clamp to max */ + else + size = size * 2 + 1; /* Stay always one less than page size, so we malloc evenly */ + } - p = realloc(buf, size + 1); - if (!p) + buf = malloc(size + 1); + if (!buf) return -ENOMEM; - buf = TAKE_PTR(p); + size = malloc_usable_size(buf) - 1; /* Use a bigger allocation if we got it anyway */ for (;;) { ssize_t k; @@ -443,25 +455,28 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re if (lseek(fd, 0, SEEK_SET) < 0) return -errno; + + buf = mfree(buf); } if (n < size) { + char *p; + + /* Return rest of the buffer to libc */ p = realloc(buf, n + 1); if (!p) return -ENOMEM; + buf = TAKE_PTR(p); } - if (!ret_size) { - /* Safety check: if the caller doesn't want to know the size of what we - * just read it will rely on the trailing NUL byte. But if there's an - * embedded NUL byte, then we should refuse operation as otherwise - * there'd be ambiguity about what we just read. */ - - if (memchr(buf, 0, n)) - return -EBADMSG; - } else + if (ret_size) *ret_size = n; + else if (memchr(buf, 0, n)) + /* Safety check: if the caller doesn't want to know the size of what we just read it will + * rely on the trailing NUL byte. But if there's an embedded NUL byte, then we should refuse + * operation as otherwise there'd be ambiguity about what we just read. */ + return -EBADMSG; buf[n] = 0; *ret_contents = TAKE_PTR(buf); @@ -546,7 +561,9 @@ int read_full_stream_full( } buf = t; - n = n_next; + /* Unless a size has been explicitly specified, try to read as much as fits into the memory + * we allocated (minus 1, to leave one byte for the safety NUL byte) */ + n = size == SIZE_MAX ? malloc_usable_size(buf) - 1 : n_next; errno = 0; k = fread(buf + l, 1, n - l, f); @@ -1146,7 +1163,7 @@ static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) { return EOL_NONE; } -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, funlockfile); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, funlockfile, NULL); int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) { size_t n = 0, allocated = 0, count = 0; @@ -1323,15 +1340,6 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c return 0; } -int sync_rights(int from, int to) { - struct stat st; - - if (fstat(from, &st) < 0) - return -errno; - - return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid); -} - int rename_and_apply_smack_floor_label(const char *from, const char *to) { int r = 0; if (rename(from, to) < 0) diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index 498e880354..64a30ab7bb 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -118,6 +118,4 @@ int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); -int sync_rights(int from, int to); - int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); diff --git a/src/libnm-systemd-shared/src/basic/format-util.c b/src/libnm-systemd-shared/src/basic/format-util.c index bf23037792..9920604f3a 100644 --- a/src/libnm-systemd-shared/src/basic/format-util.c +++ b/src/libnm-systemd-shared/src/basic/format-util.c @@ -43,17 +43,17 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { { "K", UINT64_C(1000) }, }; const suffix_table *table; - size_t n, i; + size_t n; assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_si)); - if (t == (uint64_t) -1) + if (t == UINT64_MAX) return NULL; table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_si; n = ELEMENTSOF(table_iec); - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) if (t >= table[i].factor) { if (flag & FORMAT_BYTES_BELOW_POINT) { snprintf(buf, l, diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 7f44b93726..b2a4e8036f 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -100,7 +100,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char /* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented, * fall back to a different method. */ - if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY)) + if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL) return -errno; /* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems @@ -117,7 +117,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char return 0; } - if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */ + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !IN_SET(errno, EINVAL, EPERM)) /* FAT returns EPERM on link()… */ return -errno; /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU @@ -135,34 +135,34 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char } int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = FILENAME_MAX+1; - int r; + size_t l = PATH_MAX; assert(p); assert(ret); for (;;) { - char *c; + _cleanup_free_ char *c = NULL; ssize_t n; - c = new(char, l); + c = new(char, l+1); if (!c) return -ENOMEM; - n = readlinkat(fd, p, c, l-1); - if (n < 0) { - r = -errno; - free(c); - return r; - } + n = readlinkat(fd, p, c, l); + if (n < 0) + return -errno; - if ((size_t) n < l-1) { + if ((size_t) n < l) { c[n] = 0; - *ret = c; + *ret = TAKE_PTR(c); return 0; } - free(c); + if (l > (SSIZE_MAX-1)/2) /* readlinkat() returns an ssize_t, and we want an extra byte for a + * trailing NUL, hence do an overflow check relative to SSIZE_MAX-1 + * here */ + return -EFBIG; + l *= 2; } } @@ -428,9 +428,9 @@ int symlink_idempotent(const char *from, const char *to, bool make_relative) { if (make_relative) { _cleanup_free_ char *parent = NULL; - parent = dirname_malloc(to); - if (!parent) - return -ENOMEM; + r = path_extract_directory(to, &parent); + if (r < 0) + return r; r = path_make_relative(parent, from, &relpath); if (r < 0) @@ -1413,33 +1413,45 @@ 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; - r = fd_verify_regular(fd); - if (r < 0) - return r; + assert(fd >= 0); - r = fd_get_path(fd, &path); - if (r < 0) { - log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -ENOSYS ? ", ignoring" : ""); + /* We only reasonably can do this for regular files and directories, hence check for that */ + if (fstat(fd, &st) < 0) + return -errno; - if (r == -ENOSYS) - /* If /proc is not available, we're most likely running in some - * chroot environment, and syncing the directory is not very - * important in that case. Let's just silently do nothing. */ - return 0; + if (S_ISREG(st.st_mode)) { - return r; - } + r = fd_get_path(fd, &path); + if (r < 0) { + log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", + fd, + r == -ENOSYS ? ", ignoring" : ""); - if (!path_is_absolute(path)) - return -EINVAL; + if (r == -ENOSYS) + /* If /proc is not available, we're most likely running in some + * chroot environment, and syncing the directory is not very + * important in that case. Let's just silently do nothing. */ + return 0; + + return r; + } + + if (!path_is_absolute(path)) + return -EINVAL; + + dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0); + if (dfd < 0) + return dfd; - dfd = open_parent(path, O_CLOEXEC, 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; @@ -1453,9 +1465,14 @@ int fsync_full(int fd) { /* Sync both the file and the directory */ r = fsync(fd) < 0 ? -errno : 0; - q = fsync_directory_of_file(fd); - return r < 0 ? r : q; + q = fsync_directory_of_file(fd); + if (r < 0) /* Return earlier error */ + return r; + if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a + * parent dir */ + return 0; + return q; } int fsync_path_at(int at_fd, const char *path) { @@ -1472,8 +1489,7 @@ int fsync_path_at(int at_fd, const char *path) { } else fd = at_fd; } else { - - opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC); + opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (opened_fd < 0) return -errno; @@ -1503,16 +1519,11 @@ int syncfs_path(int atfd, const char *path) { int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; - int fd; - - if (isempty(path)) - return -EINVAL; - if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */ - return -EINVAL; + int fd, r; - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; + r = path_extract_directory(path, &parent); + if (r < 0) + return r; /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ @@ -1614,7 +1625,7 @@ int path_is_encrypted(const char *path) { return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); } -int conservative_rename( +int conservative_renameat( int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { @@ -1656,23 +1667,37 @@ int conservative_rename( goto do_rename; for (;;) { - char buf1[16*1024]; - char buf2[sizeof(buf1) + 1]; + uint8_t buf1[16*1024]; + uint8_t buf2[sizeof(buf1)]; ssize_t l1, l2; l1 = read(old_fd, buf1, sizeof(buf1)); if (l1 < 0) goto do_rename; - l2 = read(new_fd, buf2, l1 + 1); - if (l1 != l2) - goto do_rename; + if (l1 == sizeof(buf1)) + /* Read the full block, hence read a full block in the other file too */ - if (l1 == 0) /* EOF on both! And everything's the same so far, yay! */ - break; + l2 = read(new_fd, buf2, l1); + else { + assert((size_t) l1 < sizeof(buf1)); + + /* Short read. This hence was the last block in the first file, and then came + * EOF. Read one byte more in the second file, so that we can verify we hit EOF there + * too. */ + + assert((size_t) (l1 + 1) <= sizeof(buf2)); + l2 = read(new_fd, buf2, l1 + 1); + } + if (l2 != l1) + goto do_rename; if (memcmp(buf1, buf2, l1) != 0) goto do_rename; + + if ((size_t) l1 < sizeof(buf1)) /* We hit EOF on the first file, and the second file too, hence exit + * now. */ + break; } is_same: diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index 9a39473567..45115fd3db 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -11,6 +11,7 @@ #include <sys/types.h> #include <unistd.h> +#include "alloc-util.h" #include "errno-util.h" #include "time-util.h" @@ -43,7 +44,8 @@ int futimens_opath(int fd, const struct timespec ts[2]); int fd_warn_permissions(const char *path, int fd); int stat_warn_permissions(const char *path, const struct stat *st); -#define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) +#define laccess(path, mode) \ + (faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) < 0 ? -errno : 0) int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode); int touch(const char *path); @@ -99,16 +101,23 @@ int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chas int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ -static inline void rmdir_and_free(char *p) { +static inline char *rmdir_and_free(char *p) { PROTECT_ERRNO; + + if (!p) + return NULL; + (void) rmdir(p); - free(p); + return mfree(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); -static inline void unlink_and_free(char *p) { +static inline char* unlink_and_free(char *p) { + if (!p) + return NULL; + (void) unlink_noerrno(p); - free(p); + return mfree(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); @@ -133,4 +142,7 @@ int open_parent(const char *path, int flags, mode_t mode); int path_is_encrypted(const char *path); -int conservative_rename(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); +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 cdc6847edf..e354c67adc 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -845,6 +845,16 @@ int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBU return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); } +int _hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) { + int r; + + r = _hashmap_ensure_allocated(h, hash_ops HASHMAP_DEBUG_PASS_ARGS); + if (r < 0) + return r; + + return hashmap_put(*h, key, value); +} + int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS) { int r; @@ -2026,3 +2036,35 @@ int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char ** *ret = TAKE_PTR(str); return 0; } + +bool set_equal(Set *a, Set *b) { + void *p; + + /* Checks whether each entry of 'a' is also in 'b' and vice versa, i.e. the two sets contain the same + * entries */ + + if (a == b) + return true; + + if (set_isempty(a) && set_isempty(b)) + return true; + + if (set_size(a) != set_size(b)) /* Cheap check that hopefully catches a lot of inequality cases + * already */ + return false; + + SET_FOREACH(p, a) + if (!set_contains(b, p)) + return false; + + /* If we have the same hashops, then we don't need to check things backwards given we compared the + * size and that all of a is in b. */ + if (a->b.hash_ops == b->b.hash_ops) + return true; + + SET_FOREACH(p, b) + if (!set_contains(a, p)) + return false; + + return true; +} diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h index e99448375e..c20ee8eb4b 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.h +++ b/src/libnm-systemd-shared/src/basic/hashmap.h @@ -133,8 +133,11 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS); #define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int _hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS); int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); + #define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define hashmap_ensure_put(s, ops, key, value) _hashmap_ensure_put(s, ops, key, value HASHMAP_DEBUG_SRC_ARGS) #define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value HASHMAP_DEBUG_PARAMS); diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c index da60202e57..cb0104670f 100644 --- a/src/libnm-systemd-shared/src/basic/hexdecoct.c +++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c @@ -119,7 +119,7 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ assert(ret_len); assert(p || l == 0); - if (l == (size_t) -1) + if (l == SIZE_MAX) l = strlen(p); /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */ @@ -309,7 +309,7 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l assert(mem); assert(_len); - if (l == (size_t) -1) + if (l == SIZE_MAX) l = strlen(p); /* padding ensures any base32hex input has input divisible by 8 */ @@ -708,7 +708,7 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r assert(ret); assert(ret_size); - if (l == (size_t) -1) + if (l == SIZE_MAX) l = strlen(p); /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index d7aba2c263..29d69910d2 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -3,14 +3,39 @@ #include <errno.h> #include <limits.h> #include <stdio.h> +#include <stdlib.h> #include <sys/utsname.h> #include <unistd.h> #include "alloc-util.h" #include "hostname-util.h" +#include "os-util.h" #include "string-util.h" #include "strv.h" +char* get_default_hostname(void) { + int r; + + const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME"); + if (e) { + if (hostname_is_valid(e, 0)) + return strdup(e); + log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e); + } + + _cleanup_free_ char *f = NULL; + r = parse_os_release(NULL, "DEFAULT_HOSTNAME", &f); + if (r < 0) + log_debug_errno(r, "Failed to parse os-release, ignoring: %m"); + else if (f) { + if (hostname_is_valid(f, 0)) + return TAKE_PTR(f); + log_debug("Invalid hostname in os-release, ignoring: %s", f); + } + + return strdup(FALLBACK_HOSTNAME); +} + char* gethostname_malloc(void) { struct utsname u; const char *s; @@ -23,7 +48,7 @@ char* gethostname_malloc(void) { s = u.nodename; if (isempty(s) || streq(s, "(none)")) - s = FALLBACK_HOSTNAME; + return get_default_hostname(); return strdup(s); } @@ -31,6 +56,7 @@ char* gethostname_malloc(void) { char* gethostname_short_malloc(void) { struct utsname u; const char *s; + _cleanup_free_ char *f = NULL; /* Like above, but kills the FQDN part if present. */ @@ -38,7 +64,10 @@ char* gethostname_short_malloc(void) { s = u.nodename; if (isempty(s) || streq(s, "(none)") || s[0] == '.') { - s = FALLBACK_HOSTNAME; + s = f = get_default_hostname(); + if (!s) + return NULL; + assert(s[0] != '.'); } diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h index 6cff9c1d4c..576ca083e0 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -7,6 +7,7 @@ #include "macro.h" #include "strv.h" +char* get_default_hostname(void); char* gethostname_malloc(void); char* gethostname_short_malloc(void); int gethostname_strict(char **ret); diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index a4f13b620a..f7a7252f1a 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -24,6 +24,12 @@ bool in4_addr_is_null(const struct in_addr *a) { return a->s_addr == 0; } +bool in6_addr_is_null(const struct in6_addr *a) { + assert(a); + + return IN6_IS_ADDR_UNSPECIFIED(a); +} + int in_addr_is_null(int family, const union in_addr_union *u) { assert(u); @@ -31,7 +37,7 @@ int in_addr_is_null(int family, const union in_addr_union *u) { return in4_addr_is_null(&u->in); if (family == AF_INET6) - return IN6_IS_ADDR_UNSPECIFIED(&u->in6); + return in6_addr_is_null(&u->in6); return -EAFNOSUPPORT; } @@ -42,6 +48,12 @@ bool in4_addr_is_link_local(const struct in_addr *a) { return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); } +bool in6_addr_is_link_local(const struct in6_addr *a) { + assert(a); + + return IN6_IS_ADDR_LINKLOCAL(a); +} + int in_addr_is_link_local(int family, const union in_addr_union *u) { assert(u); @@ -49,7 +61,7 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { return in4_addr_is_link_local(&u->in); if (family == AF_INET6) - return IN6_IS_ADDR_LINKLOCAL(&u->in6); + return in6_addr_is_link_local(&u->in6); return -EAFNOSUPPORT; } @@ -116,6 +128,13 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b) { return a->s_addr == b->s_addr; } +bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) { + assert(a); + assert(b); + + return IN6_ARE_ADDR_EQUAL(a, b); +} + int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { assert(a); assert(b); @@ -124,7 +143,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_ return in4_addr_equal(&a->in, &b->in); if (family == AF_INET6) - return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6); + return in6_addr_equal(&a->in6, &b->in6); return -EAFNOSUPPORT; } @@ -189,8 +208,8 @@ int in_addr_prefix_intersect( int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { assert(u); - /* Increases the network part of an address by one. Returns - * positive if that succeeds, or -ERANGE if this overflows. */ + /* Increases the network part of an address by one. Returns 0 if that succeeds, or -ERANGE if + * this overflows. */ return in_addr_prefix_nth(family, u, prefixlen, 1); } @@ -198,19 +217,17 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) /* * Calculates the nth prefix of size prefixlen starting from the address denoted by u. * - * On success 1 will be returned and the calculated prefix will be available in - * u. In the case nth == 0 the input will be left unchanged and 1 will be returned. - * In case the calculation cannot be performed (invalid prefix length, + * On success 0 will be returned and the calculated prefix will be available in + * u. In case the calculation cannot be performed (invalid prefix length, * overflows would occur) -ERANGE is returned. If the address family given isn't * supported -EAFNOSUPPORT will be returned. * - * * Examples: - * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u - * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 0, writes 192.168.2.0 to u + * - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 0, no data written * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written * - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written - * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u + * - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 0, writes 2001:0db8:0000:ff00:: to u */ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) { assert(u); @@ -218,13 +235,11 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u if (prefixlen <= 0) return -ERANGE; - if (nth == 0) - return 1; - if (family == AF_INET) { uint32_t c, n, t; + if (prefixlen > 32) - prefixlen = 32; + return -ERANGE; c = be32toh(u->in.s_addr); @@ -238,44 +253,40 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen); u->in.s_addr = htobe32(n); - return 1; + return 0; } if (family == AF_INET6) { - struct in6_addr result = {}; - uint8_t overflow = 0; - uint64_t delta; /* this assumes that we only ever have to up to 1<<64 subnets */ - unsigned start_byte = (prefixlen - 1) / 8; + bool overflow = false; if (prefixlen > 128) - prefixlen = 128; - - /* First calculate what we have to add */ - delta = nth << ((128 - prefixlen) % 8); + return -ERANGE; for (unsigned i = 16; i > 0; i--) { - unsigned j = i - 1; - unsigned d = 0; - - if (j <= start_byte) { - int16_t t; + unsigned t, j = i - 1, p = j * 8; - d = delta & 0xFF; - delta >>= 8; + if (p >= prefixlen) { + u->in6.s6_addr[j] = 0; + continue; + } - t = u->in6.s6_addr[j] + d + overflow; - overflow = t > UINT8_MAX ? t - UINT8_MAX : 0; + if (prefixlen - p < 8) { + u->in6.s6_addr[j] &= 0xff << (8 - (prefixlen - p)); + t = u->in6.s6_addr[j] + ((nth & 0xff) << (8 - (prefixlen - p))); + nth >>= prefixlen - p; + } else { + t = u->in6.s6_addr[j] + (nth & 0xff) + overflow; + nth >>= 8; + } - result.s6_addr[j] = (uint8_t)t; - } else - result.s6_addr[j] = u->in6.s6_addr[j]; + overflow = t > UINT8_MAX; + u->in6.s6_addr[j] = (uint8_t) (t & 0xff); } - if (overflow || delta != 0) + if (overflow || nth != 0) return -ERANGE; - u->in6 = result; - return 1; + return 0; } return -EAFNOSUPPORT; @@ -358,6 +369,43 @@ int in_addr_random_prefix( return -EAFNOSUPPORT; } +int in_addr_prefix_range( + int family, + const union in_addr_union *in, + unsigned prefixlen, + union in_addr_union *ret_start, + union in_addr_union *ret_end) { + + union in_addr_union start, end; + int r; + + assert(in); + + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + if (ret_start) { + start = *in; + r = in_addr_prefix_nth(family, &start, prefixlen, 0); + if (r < 0) + return r; + } + + if (ret_end) { + end = *in; + r = in_addr_prefix_nth(family, &end, prefixlen, 1); + if (r < 0) + return r; + } + + if (ret_start) + *ret_start = start; + if (ret_end) + *ret_end = end; + + return 0; +} + int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { _cleanup_free_ char *x = NULL; size_t l; diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index 24308b702e..519ee53b3a 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -21,11 +21,29 @@ struct in_addr_data { }; bool in4_addr_is_null(const struct in_addr *a); +static inline bool in4_addr_is_set(const struct in_addr *a) { + return !in4_addr_is_null(a); +} +bool in6_addr_is_null(const struct in6_addr *a); +static inline bool in6_addr_is_set(const struct in6_addr *a) { + return !in6_addr_is_null(a); +} int in_addr_is_null(int family, const union in_addr_union *u); +static inline bool in_addr_is_set(int family, const union in_addr_union *u) { + return in_addr_is_null(family, u) == 0; +} +static inline int in_addr_data_is_null(const struct in_addr_data *a) { + assert(a); + return in_addr_is_null(a->family, &a->address); +} +static inline bool in_addr_data_is_set(const struct in_addr_data *a) { + return in_addr_data_is_null(a); +} int in_addr_is_multicast(int family, const union in_addr_union *u); bool in4_addr_is_link_local(const struct in_addr *a); +bool in6_addr_is_link_local(const struct in6_addr *a); int in_addr_is_link_local(int family, const union in_addr_union *u); bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); @@ -36,11 +54,18 @@ bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a); bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b); +bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth); int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); +int in_addr_prefix_range( + int family, + const union in_addr_union *in, + unsigned prefixlen, + union in_addr_union *ret_start, + union in_addr_union *ret_end); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index 4d7405296b..f0a66da9bf 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -2,7 +2,6 @@ #include <errno.h> #include <limits.h> -#include <poll.h> #include <stdio.h> #include <unistd.h> @@ -159,24 +158,42 @@ int pipe_eof(int fd) { return !!(r & POLLHUP); } -int fd_wait_for_event(int fd, int event, usec_t t) { - - struct pollfd pollfd = { - .fd = fd, - .events = event, - }; - +int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { struct timespec ts; int r; - r = ppoll(&pollfd, 1, t == USEC_INFINITY ? NULL : timespec_store(&ts, t), NULL); + assert(fds || nfds == 0); + + if (nfds == 0) + return 0; + + r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL); if (r < 0) return -errno; if (r == 0) return 0; - if (pollfd.revents & POLLNVAL) - return -EBADF; + for (size_t i = 0, n = r; i < nfds && n > 0; i++) { + if (fds[i].revents == 0) + continue; + if (fds[i].revents & POLLNVAL) + return -EBADF; + n--; + } + + return r; +} + +int fd_wait_for_event(int fd, int event, usec_t timeout) { + struct pollfd pollfd = { + .fd = fd, + .events = event, + }; + int r; + + r = ppoll_usec(&pollfd, 1, timeout); + if (r <= 0) + return r; return pollfd.revents; } @@ -319,16 +336,14 @@ int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, ch } void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { - size_t i; - - for (i = 0; i < iovw->count; i++) + for (size_t i = 0; i < iovw->count; i++) iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new; } size_t iovw_size(struct iovec_wrapper *iovw) { - size_t n = 0, i; + size_t n = 0; - for (i = 0; i < iovw->count; i++) + for (size_t i = 0; i < iovw->count; i++) n += iovw->iovec[i].iov_len; return n; diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h index d817714b05..d98817f760 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.h +++ b/src/libnm-systemd-shared/src/basic/io-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <poll.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -18,6 +19,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); int pipe_eof(int fd); +int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout); int fd_wait_for_event(int fd, int event, usec_t timeout); ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); @@ -60,7 +62,7 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { /* Same as above, but allows one extra value: -1 as indication for infinity. */ - if (l == (uint64_t) -1) + if (l == UINT64_MAX) return true; return FILE_SIZE_VALID(l); diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 41d828fd98..b3d32abfc8 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -12,16 +12,6 @@ struct iovec; struct signalfd_siginfo; -typedef enum LogRealm { - LOG_REALM_SYSTEMD, - LOG_REALM_UDEV, - _LOG_REALM_MAX, -} LogRealm; - -#ifndef LOG_REALM -# define LOG_REALM LOG_REALM_SYSTEMD -#endif - typedef enum LogTarget{ LOG_TARGET_CONSOLE, LOG_TARGET_CONSOLE_PREFIXED, @@ -33,35 +23,26 @@ typedef enum LogTarget{ LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */ LOG_TARGET_NULL, _LOG_TARGET_MAX, - _LOG_TARGET_INVALID = -1 + _LOG_TARGET_INVALID = -EINVAL, } LogTarget; /* Note to readers: << and >> have lower precedence than & and | */ -#define LOG_REALM_PLUS_LEVEL(realm, level) ((realm) << 10 | (level)) -#define LOG_REALM_REMOVE_LEVEL(realm_level) ((realm_level) >> 10) #define SYNTHETIC_ERRNO(num) (1 << 30 | (num)) #define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) #define ERRNO_VALUE(val) (abs(val) & 255) +const char *log_target_to_string(LogTarget target) _const_; +LogTarget log_target_from_string(const char *s) _pure_; void log_set_target(LogTarget target); +int log_set_target_from_string(const char *e); +LogTarget log_get_target(void) _pure_; -void log_set_max_level_realm(LogRealm realm, int level); - -#define log_set_max_level(level) \ - log_set_max_level_realm(LOG_REALM, (level)) - -static inline void log_set_max_level_all_realms(int level) { - for (LogRealm realm = 0; realm < _LOG_REALM_MAX; realm++) - log_set_max_level_realm(realm, level); -} +void log_set_max_level(int level); +int log_set_max_level_from_string(const char *e); +int log_get_max_level(void) _pure_; void log_set_facility(int facility); -int log_set_target_from_string(const char *e); -int log_set_max_level_from_string_realm(LogRealm realm, const char *e); -#define log_set_max_level_from_string(e) \ - log_set_max_level_from_string_realm(LOG_REALM, (e)) - void log_show_color(bool b); bool log_get_show_color(void) _pure_; void log_show_location(bool b); @@ -76,15 +57,9 @@ int log_show_location_from_string(const char *e); int log_show_time_from_string(const char *e); int log_show_tid_from_string(const char *e); -LogTarget log_get_target(void) _pure_; -int log_get_max_level_realm(LogRealm realm) _pure_; -#define log_get_max_level() \ - log_get_max_level_realm(LOG_REALM) - /* Functions below that open and close logs or configure logging based on the * environment should not be called from library code — this is always a job - * for the application itself. - */ + * for the application itself. */ assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1); #define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1]) @@ -93,12 +68,7 @@ int log_open(void); void log_close(void); void log_forget_fds(void); -void log_parse_environment_realm(LogRealm realm); -void log_parse_environment_cli_realm(LogRealm realm); -#define log_parse_environment() \ - log_parse_environment_realm(LOG_REALM) -#define log_parse_environment_cli() \ - log_parse_environment_cli_realm(LOG_REALM) +void log_parse_environment(void); int log_dispatch_internal( int level, @@ -112,17 +82,15 @@ int log_dispatch_internal( const char *extra_field, char *buffer); -int log_internal_realm( +int log_internal( int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(6,7); -#define log_internal(level, ...) \ - log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) -int log_internalv_realm( +int log_internalv( int level, int error, const char *file, @@ -130,10 +98,7 @@ int log_internalv_realm( const char *func, const char *format, va_list ap) _printf_(6,0); -#define log_internalv(level, ...) \ - log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) -/* Realm is fixed to LOG_REALM_SYSTEMD for those */ int log_object_internalv( int level, int error, @@ -201,49 +166,36 @@ int log_dump_internal( char *buffer); /* Logging for various assertions */ -_noreturn_ void log_assert_failed_realm( - LogRealm realm, +_noreturn_ void log_assert_failed( const char *text, const char *file, int line, const char *func); -#define log_assert_failed(text, ...) \ - log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) -_noreturn_ void log_assert_failed_unreachable_realm( - LogRealm realm, +_noreturn_ void log_assert_failed_unreachable( const char *text, const char *file, int line, const char *func); -#define log_assert_failed_unreachable(text, ...) \ - log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__) -void log_assert_failed_return_realm( - LogRealm realm, +void log_assert_failed_return( const char *text, const char *file, int line, const char *func); -#define log_assert_failed_return(text, ...) \ - log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__) #define log_dispatch(level, error, buffer) \ log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer) /* Logging with level */ -#define log_full_errno_realm(realm, level, error, ...) \ +#define log_full_errno(level, error, ...) \ ({ \ - int _level = (level), _e = (error), _realm = (realm); \ - (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \ - ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \ - PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ + int _level = (level), _e = (error); \ + (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ : -ERRNO_VALUE(_e); \ }) -#define log_full_errno(level, error, ...) \ - log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) - #define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__) int log_emergency_level(void); @@ -264,36 +216,30 @@ int log_emergency_level(void); #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) #define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) -#ifdef LOG_TRACE +#if LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) #else # define log_trace(...) do {} while (0) #endif /* Structured logging */ -#define log_struct_errno(level, error, ...) \ - log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__, NULL) +#define log_struct_errno(level, error, ...) \ + log_struct_internal(level, error, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__, NULL) #define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) #define log_struct_iovec_errno(level, error, iovec, n_iovec) \ - log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - error, PROJECT_FILE, __LINE__, __func__, iovec, n_iovec) + log_struct_iovec_internal(level, error, PROJECT_FILE, __LINE__, __func__, iovec, n_iovec) #define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec) /* This modifies the buffer passed! */ -#define log_dump(level, buffer) \ - log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ - 0, PROJECT_FILE, __LINE__, __func__, buffer) +#define log_dump(level, buffer) \ + log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer) -#define log_oom() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_ERR), PROJECT_FILE, __LINE__, __func__) -#define log_oom_debug() log_oom_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, LOG_DEBUG), PROJECT_FILE, __LINE__, __func__) +#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__) +#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__) bool log_on_console(void) _pure_; -const char *log_target_to_string(LogTarget target) _const_; -LogTarget log_target_from_string(const char *s) _pure_; - /* Helper to prepare various field for structured logging */ #define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ @@ -354,5 +300,4 @@ int log_syntax_invalid_utf8_internal( #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) -void log_setup_service(void); -void log_setup_cli(void); +void log_setup(void); diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index 2782553756..f1d5e0894e 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -9,6 +9,8 @@ #include <sys/sysmacros.h> #include <sys/types.h> +#include "macro-fundamental.h" + #define _printf_(a, b) __attribute__((__format__(printf, a, b))) #ifdef __clang__ # define _alloc_(...) @@ -18,10 +20,7 @@ #define _sentinel_ __attribute__((__sentinel__)) #define _section_(x) __attribute__((__section__(x))) #define _used_ __attribute__((__used__)) -#define _unused_ __attribute__((__unused__)) #define _destructor_ __attribute__((__destructor__)) -#define _pure_ __attribute__((__pure__)) -#define _const_ __attribute__((__const__)) #define _deprecated_ __attribute__((__deprecated__)) #define _packed_ __attribute__((__packed__)) #define _malloc_ __attribute__((__malloc__)) @@ -34,7 +33,6 @@ #define _align_(x) __attribute__((__aligned__(x))) #define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) #define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) -#define _cleanup_(x) __attribute__((__cleanup__(x))) #if __GNUC__ >= 7 #define _fallthrough_ __attribute__((__fallthrough__)) #else @@ -143,12 +141,6 @@ #define XSTRINGIFY(x) #x #define STRINGIFY(x) XSTRINGIFY(x) -#define XCONCATENATE(x, y) x ## y -#define CONCATENATE(x, y) XCONCATENATE(x, y) - -#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) -#define UNIQ __COUNTER__ - /* builtins */ #if __SIZEOF_INT__ == 4 #define BUILTIN_FFS_U32(x) __builtin_ffs(x); @@ -222,18 +214,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { return m; } -#ifndef __COVERITY__ -# define VOID_0 ((void)0) -#else -# define VOID_0 ((void*)0) -#endif - -#define ELEMENTSOF(x) \ - (__builtin_choose_expr( \ - !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ - sizeof(x)/sizeof((x)[0]), \ - VOID_0)) - /* * STRLEN - return the length of a string literal, minus the trailing NUL byte. * Contrary to strlen(), this is a constant expression. @@ -254,106 +234,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \ }) -#undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) -#define __MAX(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MAX(_A, _B) \ - (__builtin_choose_expr( \ - __builtin_constant_p(_A) && \ - __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ - ((_A) > (_B)) ? (_A) : (_B), \ - VOID_0)) - -/* takes two types and returns the size of the larger one */ -#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) - -#define MAX3(x, y, z) \ - ({ \ - const typeof(x) _c = MAX(x, y); \ - MAX(_c, z); \ - }) - -#define MAX4(x, y, z, a) \ - ({ \ - const typeof(x) _d = MAX3(x, y, z); \ - MAX(_d, a); \ - }) - -#undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) -#define __MIN(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MIN(_A, _B) \ - (__builtin_choose_expr( \ - __builtin_constant_p(_A) && \ - __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ - ((_A) < (_B)) ? (_A) : (_B), \ - VOID_0)) - -#define MIN3(x, y, z) \ - ({ \ - const typeof(x) _c = MIN(x, y); \ - MIN(_c, z); \ - }) - -#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) -#define __LESS_BY(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \ - }) - -#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) -#define __CMP(aq, a, bq, b) \ - ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ - UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ - }) - -#undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) -#define __CLAMP(xq, x, lowq, low, highq, high) \ - ({ \ - const typeof(x) UNIQ_T(X, xq) = (x); \ - const typeof(low) UNIQ_T(LOW, lowq) = (low); \ - const typeof(high) UNIQ_T(HIGH, highq) = (high); \ - UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \ - UNIQ_T(HIGH, highq) : \ - UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \ - UNIQ_T(LOW, lowq) : \ - UNIQ_T(X, xq); \ - }) - -/* [(x + y - 1) / y] suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the - * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) -#define __DIV_ROUND_UP(xq, x, yq, y) \ - ({ \ - const typeof(x) UNIQ_T(X, xq) = (x); \ - const typeof(y) UNIQ_T(Y, yq) = (y); \ - (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \ - }) - #ifdef __COVERITY__ /* Use special definitions of assertion macros in order to prevent @@ -410,16 +290,6 @@ static inline int __coverity_check_and_return__(int condition) { #define assert_not_reached(t) \ log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__) -#if defined(static_assert) -#define assert_cc(expr) \ - static_assert(expr, #expr) -#else -#define assert_cc(expr) \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - } -#endif - #define assert_return(expr, r) \ do { \ if (!assert_log(expr, #expr)) \ @@ -498,53 +368,6 @@ static inline int __coverity_check_and_return__(int condition) { #define FLAGS_SET(v, flags) \ ((~(v) & (flags)) == 0) -#define CASE_F(X) case X: -#define CASE_F_1(CASE, X) CASE_F(X) -#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) -#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) -#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) -#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) -#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) -#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) -#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) -#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) -#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) -#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) -#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) -#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) -#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) -#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) -#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) -#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) -#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) -#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) -#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) - -#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME -#define FOR_EACH_MAKE_CASE(...) \ - GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ - CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ - (CASE_F,__VA_ARGS__) - -#define IN_SET(x, ...) \ - ({ \ - bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ - * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ - * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ - * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ - static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \ - assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ - break; \ - default: \ - break; \ - } \ - _found; \ - }) - #define SWAP_TWO(x, y) do { \ typeof(x) _t = (x); \ (x) = (y); \ @@ -553,6 +376,7 @@ static inline int __coverity_check_and_return__(int condition) { #define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) #define STRV_MAKE_EMPTY ((char*[1]) { NULL }) +#define STRV_MAKE_CONST(...) ((const char* const*) ((const char*[]) { __VA_ARGS__, NULL })) /* Pointers range from NULL to POINTER_MAX */ #define POINTER_MAX ((void*) UINTPTR_MAX) @@ -582,10 +406,20 @@ static inline int __coverity_check_and_return__(int condition) { func(p); \ } +/* When func() returns the void value (NULL, -1, …) of the appropriate type */ #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ + *p = func(*p); \ + } + +/* When func() doesn't return the appropriate type, set variable to empty afterwards */ +#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \ + static inline void func##p(type *p) { \ + if (*p != (empty)) { \ func(*p); \ + *p = (empty); \ + } \ } #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \ diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index 06166b3fb3..1384324804 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -5,6 +5,11 @@ #include <errno.h> #include <fcntl.h> +#if HAVE_LINUX_TIME_TYPES_H +/* This header defines __kernel_timespec for us, but is only available since Linux 5.1, hence conditionally + * include this. */ +#include <linux/time_types.h> +#endif #include <signal.h> #include <sys/syscall.h> #include <sys/types.h> @@ -15,34 +20,17 @@ #include <asm/sgidefs.h> #endif -#if defined(__alpha__) -# define systemd_SC_arch_bias(x) (110 + (x)) -#elif defined(__ia64__) -# define systemd_SC_arch_bias(x) (1024 + (x)) -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_SC_arch_bias(x) (4000 + (x)) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_SC_arch_bias(x) (6000 + (x)) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_SC_arch_bias(x) (5000 + (x)) -# else -# error "Unknown MIPS ABI" -# endif -#elif defined(__x86_64__) && defined(__ILP32__) -# define systemd_SC_arch_bias(x) ((x) | /* __X32_SYSCALL_BIT */ 0x40000000) -#else -# define systemd_SC_arch_bias(x) (x) -#endif - #include "missing_keyctl.h" #include "missing_stat.h" +#include "missing_syscall_def.h" /* linux/kcmp.h */ #ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */ #define KCMP_FILE 0 #endif +/* ======================================================================= */ + #if !HAVE_PIVOT_ROOT static inline int missing_pivot_root(const char *new_root, const char *put_old) { return syscall(__NR_pivot_root, new_root, put_old); @@ -53,54 +41,6 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old) /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_memfd_create 279 -#elif defined(__alpha__) -# define systemd_NR_memfd_create 512 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_memfd_create 279 -#elif defined(__arm__) -# define systemd_NR_memfd_create 385 -#elif defined(__i386__) -# define systemd_NR_memfd_create 356 -#elif defined(__ia64__) -# define systemd_NR_memfd_create systemd_SC_arch_bias(316) -#elif defined(__m68k__) -# define systemd_NR_memfd_create 353 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_memfd_create systemd_SC_arch_bias(354) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_memfd_create systemd_SC_arch_bias(318) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_memfd_create systemd_SC_arch_bias(314) -# endif -#elif defined(__powerpc__) -# define systemd_NR_memfd_create 360 -#elif defined(__s390__) -# define systemd_NR_memfd_create 350 -#elif defined(__sparc__) -# define systemd_NR_memfd_create 348 -#elif defined(__x86_64__) -# define systemd_NR_memfd_create systemd_SC_arch_bias(319) -#else -# warning "memfd_create() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_memfd_create && __NR_memfd_create >= 0 -# if defined systemd_NR_memfd_create -assert_cc(__NR_memfd_create == systemd_NR_memfd_create); -# endif -#else -# if defined __NR_memfd_create -# undef __NR_memfd_create -# endif -# if defined systemd_NR_memfd_create -# define __NR_memfd_create systemd_NR_memfd_create -# endif -#endif - #if !HAVE_MEMFD_CREATE static inline int missing_memfd_create(const char *name, unsigned int flags) { # ifdef __NR_memfd_create @@ -116,54 +56,6 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) { /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_getrandom 278 -#elif defined(__alpha__) -# define systemd_NR_getrandom 511 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_getrandom 278 -#elif defined(__arm__) -# define systemd_NR_getrandom 384 -#elif defined(__i386__) -# define systemd_NR_getrandom 355 -#elif defined(__ia64__) -# define systemd_NR_getrandom systemd_SC_arch_bias(318) -#elif defined(__m68k__) -# define systemd_NR_getrandom 352 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_getrandom systemd_SC_arch_bias(353) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_getrandom systemd_SC_arch_bias(317) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_getrandom systemd_SC_arch_bias(313) -# endif -#elif defined(__powerpc__) -# define systemd_NR_getrandom 359 -#elif defined(__s390__) -# define systemd_NR_getrandom 349 -#elif defined(__sparc__) -# define systemd_NR_getrandom 347 -#elif defined(__x86_64__) -# define systemd_NR_getrandom systemd_SC_arch_bias(318) -#else -# warning "getrandom() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_getrandom && __NR_getrandom >= 0 -# if defined systemd_NR_getrandom -assert_cc(__NR_getrandom == systemd_NR_getrandom); -# endif -#else -# if defined __NR_getrandom -# undef __NR_getrandom -# endif -# if defined systemd_NR_getrandom -# define __NR_getrandom systemd_NR_getrandom -# endif -#endif - #if !HAVE_GETRANDOM static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) { # ifdef __NR_getrandom @@ -194,54 +86,6 @@ static inline pid_t missing_gettid(void) { /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_name_to_handle_at 264 -#elif defined(__alpha__) -# define systemd_NR_name_to_handle_at 497 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_name_to_handle_at 264 -#elif defined(__arm__) -# define systemd_NR_name_to_handle_at 370 -#elif defined(__i386__) -# define systemd_NR_name_to_handle_at 341 -#elif defined(__ia64__) -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(302) -#elif defined(__m68k__) -# define systemd_NR_name_to_handle_at 340 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(339) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(298) -# endif -#elif defined(__powerpc__) -# define systemd_NR_name_to_handle_at 345 -#elif defined(__s390__) -# define systemd_NR_name_to_handle_at 335 -#elif defined(__sparc__) -# define systemd_NR_name_to_handle_at 332 -#elif defined(__x86_64__) -# define systemd_NR_name_to_handle_at systemd_SC_arch_bias(303) -#else -# warning "name_to_handle_at() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0 -# if defined systemd_NR_name_to_handle_at -assert_cc(__NR_name_to_handle_at == systemd_NR_name_to_handle_at); -# endif -#else -# if defined __NR_name_to_handle_at -# undef __NR_name_to_handle_at -# endif -# if defined systemd_NR_name_to_handle_at -# define __NR_name_to_handle_at systemd_NR_name_to_handle_at -# endif -#endif - #if !HAVE_NAME_TO_HANDLE_AT struct file_handle { unsigned int handle_bytes; @@ -263,54 +107,6 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_setns 268 -#elif defined(__alpha__) -# define systemd_NR_setns 501 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_setns 268 -#elif defined(__arm__) -# define systemd_NR_setns 375 -#elif defined(__i386__) -# define systemd_NR_setns 346 -#elif defined(__ia64__) -# define systemd_NR_setns systemd_SC_arch_bias(306) -#elif defined(__m68k__) -# define systemd_NR_setns 344 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_setns systemd_SC_arch_bias(344) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_setns systemd_SC_arch_bias(308) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_setns systemd_SC_arch_bias(303) -# endif -#elif defined(__powerpc__) -# define systemd_NR_setns 350 -#elif defined(__s390__) -# define systemd_NR_setns 339 -#elif defined(__sparc__) -# define systemd_NR_setns 337 -#elif defined(__x86_64__) -# define systemd_NR_setns systemd_SC_arch_bias(308) -#else -# warning "setns() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_setns && __NR_setns >= 0 -# if defined systemd_NR_setns -assert_cc(__NR_setns == systemd_NR_setns); -# endif -#else -# if defined __NR_setns -# undef __NR_setns -# endif -# if defined systemd_NR_setns -# define __NR_setns systemd_NR_setns -# endif -#endif - #if !HAVE_SETNS static inline int missing_setns(int fd, int nstype) { # ifdef __NR_setns @@ -336,54 +132,6 @@ static inline pid_t raw_getpid(void) { /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_renameat2 276 -#elif defined(__alpha__) -# define systemd_NR_renameat2 510 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_renameat2 276 -#elif defined(__arm__) -# define systemd_NR_renameat2 382 -#elif defined(__i386__) -# define systemd_NR_renameat2 353 -#elif defined(__ia64__) -# define systemd_NR_renameat2 systemd_SC_arch_bias(314) -#elif defined(__m68k__) -# define systemd_NR_renameat2 351 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_renameat2 systemd_SC_arch_bias(351) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_renameat2 systemd_SC_arch_bias(315) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_renameat2 systemd_SC_arch_bias(311) -# endif -#elif defined(__powerpc__) -# define systemd_NR_renameat2 357 -#elif defined(__s390__) -# define systemd_NR_renameat2 347 -#elif defined(__sparc__) -# define systemd_NR_renameat2 345 -#elif defined(__x86_64__) -# define systemd_NR_renameat2 systemd_SC_arch_bias(316) -#else -# warning "renameat2() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_renameat2 && __NR_renameat2 >= 0 -# if defined systemd_NR_renameat2 -assert_cc(__NR_renameat2 == systemd_NR_renameat2); -# endif -#else -# if defined __NR_renameat2 -# undef __NR_renameat2 -# endif -# if defined systemd_NR_renameat2 -# define __NR_renameat2 systemd_NR_renameat2 -# endif -#endif - #if !HAVE_RENAMEAT2 static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { # ifdef __NR_renameat2 @@ -451,54 +199,6 @@ static inline key_serial_t missing_request_key(const char *type, const char *des /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_copy_file_range 285 -#elif defined(__alpha__) -# define systemd_NR_copy_file_range 519 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_copy_file_range 285 -#elif defined(__arm__) -# define systemd_NR_copy_file_range 391 -#elif defined(__i386__) -# define systemd_NR_copy_file_range 377 -#elif defined(__ia64__) -# define systemd_NR_copy_file_range systemd_SC_arch_bias(323) -#elif defined(__m68k__) -# define systemd_NR_copy_file_range 376 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_copy_file_range systemd_SC_arch_bias(360) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_copy_file_range systemd_SC_arch_bias(324) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_copy_file_range systemd_SC_arch_bias(320) -# endif -#elif defined(__powerpc__) -# define systemd_NR_copy_file_range 379 -#elif defined(__s390__) -# define systemd_NR_copy_file_range 375 -#elif defined(__sparc__) -# define systemd_NR_copy_file_range 357 -#elif defined(__x86_64__) -# define systemd_NR_copy_file_range systemd_SC_arch_bias(326) -#else -# warning "copy_file_range() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_copy_file_range && __NR_copy_file_range >= 0 -# if defined systemd_NR_copy_file_range -assert_cc(__NR_copy_file_range == systemd_NR_copy_file_range); -# endif -#else -# if defined __NR_copy_file_range -# undef __NR_copy_file_range -# endif -# if defined systemd_NR_copy_file_range -# define __NR_copy_file_range systemd_NR_copy_file_range -# endif -#endif - #if !HAVE_COPY_FILE_RANGE static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, @@ -517,54 +217,6 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, /* ======================================================================= */ -#if defined(__aarch64__) -# define systemd_NR_bpf 280 -#elif defined(__alpha__) -# define systemd_NR_bpf 515 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_bpf 280 -#elif defined(__arm__) -# define systemd_NR_bpf 386 -#elif defined(__i386__) -# define systemd_NR_bpf 357 -#elif defined(__ia64__) -# define systemd_NR_bpf systemd_SC_arch_bias(317) -#elif defined(__m68k__) -# define systemd_NR_bpf 354 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_bpf systemd_SC_arch_bias(355) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_bpf systemd_SC_arch_bias(319) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_bpf systemd_SC_arch_bias(315) -# endif -#elif defined(__powerpc__) -# define systemd_NR_bpf 361 -#elif defined(__s390__) -# define systemd_NR_bpf 351 -#elif defined(__sparc__) -# define systemd_NR_bpf 349 -#elif defined(__x86_64__) -# define systemd_NR_bpf systemd_SC_arch_bias(321) -#else -# warning "bpf() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_bpf && __NR_bpf >= 0 -# if defined systemd_NR_bpf -assert_cc(__NR_bpf == systemd_NR_bpf); -# endif -#else -# if defined __NR_bpf -# undef __NR_bpf -# endif -# if defined systemd_NR_bpf -# define __NR_bpf systemd_NR_bpf -# endif -#endif - #if !HAVE_BPF union bpf_attr; @@ -582,106 +234,6 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) { /* ======================================================================= */ -#ifndef __IGNORE_pkey_mprotect -# if defined(__aarch64__) -# define systemd_NR_pkey_mprotect 288 -# elif defined(__alpha__) -# define systemd_NR_pkey_mprotect 524 -# elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_pkey_mprotect 226 -# elif defined(__arm__) -# define systemd_NR_pkey_mprotect 394 -# elif defined(__i386__) -# define systemd_NR_pkey_mprotect 380 -# elif defined(__ia64__) -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(330) -# elif defined(__m68k__) -# define systemd_NR_pkey_mprotect 381 -# elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(363) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(327) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(323) -# endif -# elif defined(__powerpc__) -# define systemd_NR_pkey_mprotect 386 -# elif defined(__s390__) -# define systemd_NR_pkey_mprotect 384 -# elif defined(__sparc__) -# define systemd_NR_pkey_mprotect 362 -# elif defined(__x86_64__) -# define systemd_NR_pkey_mprotect systemd_SC_arch_bias(329) -# else -# warning "pkey_mprotect() syscall number is unknown for your architecture" -# endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -# if defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0 -# if defined systemd_NR_pkey_mprotect -assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect); -# endif -# else -# if defined __NR_pkey_mprotect -# undef __NR_pkey_mprotect -# endif -# if defined systemd_NR_pkey_mprotect -# define __NR_pkey_mprotect systemd_NR_pkey_mprotect -# endif -# endif -#endif - -/* ======================================================================= */ - -#if defined(__aarch64__) -# define systemd_NR_statx 291 -#elif defined(__alpha__) -# define systemd_NR_statx 522 -#elif defined(__arc__) || defined(__tilegx__) -# define systemd_NR_statx 291 -#elif defined(__arm__) -# define systemd_NR_statx 397 -#elif defined(__i386__) -# define systemd_NR_statx 383 -#elif defined(__ia64__) -# define systemd_NR_statx systemd_SC_arch_bias(326) -#elif defined(__m68k__) -# define systemd_NR_statx 379 -#elif defined(_MIPS_SIM) -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define systemd_NR_statx systemd_SC_arch_bias(366) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define systemd_NR_statx systemd_SC_arch_bias(330) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define systemd_NR_statx systemd_SC_arch_bias(326) -# endif -#elif defined(__powerpc__) -# define systemd_NR_statx 383 -#elif defined(__s390__) -# define systemd_NR_statx 379 -#elif defined(__sparc__) -# define systemd_NR_statx 360 -#elif defined(__x86_64__) -# define systemd_NR_statx systemd_SC_arch_bias(332) -#else -# warning "statx() syscall number is unknown for your architecture" -#endif - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_statx && __NR_statx >= 0 -# if defined systemd_NR_statx -assert_cc(__NR_statx == systemd_NR_statx); -# endif -#else -# if defined __NR_statx -# undef __NR_statx -# endif -# if defined systemd_NR_statx -# define __NR_statx systemd_NR_statx -# endif -#endif - #if !HAVE_STATX struct statx; @@ -747,21 +299,6 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask, /* ======================================================================= */ -/* should be always defined, see kernel 39036cd2727395c3369b1051005da74059a85317 */ -#define systemd_NR_pidfd_send_signal systemd_SC_arch_bias(424) - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0 -# if defined systemd_NR_pidfd_send_signal -assert_cc(__NR_pidfd_send_signal == systemd_NR_pidfd_send_signal); -# endif -#else -# if defined __NR_pidfd_send_signal -# undef __NR_pidfd_send_signal -# endif -# define __NR_pidfd_send_signal systemd_NR_pidfd_send_signal -#endif - #if !HAVE_PIDFD_SEND_SIGNAL static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) { # ifdef __NR_pidfd_send_signal @@ -775,21 +312,6 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un # define pidfd_send_signal missing_pidfd_send_signal #endif -/* should be always defined, see kernel 7615d9e1780e26e0178c93c55b73309a5dc093d7 */ -#define systemd_NR_pidfd_open systemd_SC_arch_bias(434) - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_pidfd_open && __NR_pidfd_open >= 0 -# if defined systemd_NR_pidfd_open -assert_cc(__NR_pidfd_open == systemd_NR_pidfd_open); -# endif -#else -# if defined __NR_pidfd_open -# undef __NR_pidfd_open -# endif -# define __NR_pidfd_open systemd_NR_pidfd_open -#endif - #if !HAVE_PIDFD_OPEN static inline int missing_pidfd_open(pid_t pid, unsigned flags) { # ifdef __NR_pidfd_open @@ -838,22 +360,6 @@ static inline int missing_execveat(int dirfd, const char *pathname, /* ======================================================================= */ -#define systemd_NR_close_range systemd_SC_arch_bias(436) - -/* may be (invalid) negative number due to libseccomp, see PR 13319 */ -#if defined __NR_close_range && __NR_close_range >= 0 -# if defined systemd_NR_close_range -assert_cc(__NR_close_range == systemd_NR_close_range); -# endif -#else -# if defined __NR_close_range -# undef __NR_close_range -# endif -# if defined systemd_NR_close_range -# define __NR_close_range systemd_NR_close_range -# endif -#endif - #if !HAVE_CLOSE_RANGE static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) { # ifdef __NR_close_range @@ -881,3 +387,41 @@ static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) # define close_range missing_close_range #endif + +/* ======================================================================= */ + +#if !HAVE_EPOLL_PWAIT2 + +/* Defined to be equivalent to the kernel's _NSIG_WORDS, i.e. the size of the array of longs that is + * encapsulated by sigset_t. */ +#define KERNEL_NSIG_WORDS (64 / (sizeof(long) * 8)) +#define KERNEL_NSIG_BYTES (KERNEL_NSIG_WORDS * sizeof(long)) + +struct epoll_event; + +static inline int missing_epoll_pwait2( + int fd, + struct epoll_event *events, + int maxevents, + const struct timespec *timeout, + const sigset_t *sigset) { + +# if defined(__NR_epoll_pwait2) && HAVE_LINUX_TIME_TYPES_H + if (timeout) { + /* Convert from userspace timespec to kernel timespec */ + struct __kernel_timespec ts = { + .tv_sec = timeout->tv_sec, + .tv_nsec = timeout->tv_nsec, + }; + + return syscall(__NR_epoll_pwait2, fd, events, maxevents, &ts, sigset, sigset ? KERNEL_NSIG_BYTES : 0); + } else + return syscall(__NR_epoll_pwait2, fd, events, maxevents, NULL, sigset, sigset ? KERNEL_NSIG_BYTES : 0); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define epoll_pwait2 missing_epoll_pwait2 +#endif diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.c b/src/libnm-systemd-shared/src/basic/ordered-set.c new file mode 100644 index 0000000000..58fa8af1b7 --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/ordered-set.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fileio.h" +#include "ordered-set.h" +#include "strv.h" + +int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HASHMAP_DEBUG_PARAMS) { + if (*s) + return 0; + + *s = _ordered_set_new(ops HASHMAP_DEBUG_PASS_ARGS); + if (!*s) + return -ENOMEM; + + return 0; +} + +int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS) { + int r; + + r = _ordered_set_ensure_allocated(s, ops HASHMAP_DEBUG_PASS_ARGS); + if (r < 0) + return r; + + return ordered_set_put(*s, p); +} + +int ordered_set_consume(OrderedSet *s, void *p) { + int r; + + r = ordered_set_put(s, p); + if (r <= 0) + free(p); + + return r; +} + +int ordered_set_put_strdup(OrderedSet *s, const char *p) { + char *c; + int r; + + assert(s); + assert(p); + + c = strdup(p); + if (!c) + return -ENOMEM; + + r = ordered_set_consume(s, c); + if (r == -EEXIST) + return 0; + + return r; +} + +int ordered_set_put_strdupv(OrderedSet *s, char **l) { + int n = 0, r; + char **i; + + STRV_FOREACH(i, l) { + r = ordered_set_put_strdup(s, *i); + if (r < 0) + return r; + + n += r; + } + + return n; +} + +int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l) { + int n = 0, r; + char *p; + + /* Like ordered_set_put_strv, but for an OrderedSet of strings */ + + ORDERED_SET_FOREACH(p, l) { + r = ordered_set_put_strdup(s, p); + if (r < 0) + return r; + + n += r; + } + + return n; +} + +void ordered_set_print(FILE *f, const char *field, OrderedSet *s) { + bool space = false; + char *p; + + if (ordered_set_isempty(s)) + return; + + fputs(field, f); + + ORDERED_SET_FOREACH(p, s) + fputs_with_space(f, p, NULL, &space); + + fputc('\n', f); +} diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h new file mode 100644 index 0000000000..baf8202088 --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/ordered-set.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdio.h> + +#include "hashmap.h" + +typedef struct OrderedSet OrderedSet; + +static inline OrderedSet* _ordered_set_new(const struct hash_ops *ops HASHMAP_DEBUG_PARAMS) { + return (OrderedSet*) _ordered_hashmap_new(ops HASHMAP_DEBUG_PASS_ARGS); +} +#define ordered_set_new(ops) _ordered_set_new(ops HASHMAP_DEBUG_SRC_ARGS) + +int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HASHMAP_DEBUG_PARAMS); +#define ordered_set_ensure_allocated(s, ops) _ordered_set_ensure_allocated(s, ops HASHMAP_DEBUG_SRC_ARGS) + +int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS); +#define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) + +static inline OrderedSet* ordered_set_free(OrderedSet *s) { + return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s); +} + +static inline OrderedSet* ordered_set_free_free(OrderedSet *s) { + return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s); +} + +static inline int ordered_set_put(OrderedSet *s, void *p) { + return ordered_hashmap_put((OrderedHashmap*) s, p, p); +} + +static inline unsigned ordered_set_size(OrderedSet *s) { + return ordered_hashmap_size((OrderedHashmap*) s); +} + +static inline bool ordered_set_isempty(OrderedSet *s) { + return ordered_hashmap_isempty((OrderedHashmap*) s); +} + +static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value) { + return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL); +} + +static inline void* ordered_set_remove(OrderedSet *s, void *p) { + return ordered_hashmap_remove((OrderedHashmap*) s, p); +} + +static inline void* ordered_set_first(OrderedSet *s) { + return ordered_hashmap_first((OrderedHashmap*) s); +} + +static inline void* ordered_set_steal_first(OrderedSet *s) { + return ordered_hashmap_steal_first((OrderedHashmap*) s); +} + +static inline char** ordered_set_get_strv(OrderedSet *s) { + return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); +} + +int ordered_set_consume(OrderedSet *s, void *p); +int ordered_set_put_strdup(OrderedSet *s, const char *p); +int ordered_set_put_strdupv(OrderedSet *s, char **l); +int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l); +void ordered_set_print(FILE *f, const char *field, OrderedSet *s); + +#define _ORDERED_SET_FOREACH(e, s, i) \ + for (Iterator i = ITERATOR_FIRST; ordered_set_iterate((s), &i, (void**)&(e)); ) +#define ORDERED_SET_FOREACH(e, s) \ + _ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ)) + +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free); + +#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep) +#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep) diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index 5d4dafe3a5..b79c885dfd 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -16,9 +16,6 @@ #include "missing_network.h" #include "parse-util.h" #include "process-util.h" -#if HAVE_SECCOMP -#include "seccomp-util.h" -#endif #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -317,46 +314,6 @@ int parse_errno(const char *t) { return e; } -#if HAVE_SECCOMP -int parse_syscall_and_errno(const char *in, char **name, int *error) { - _cleanup_free_ char *n = NULL; - char *p; - int e = -1; - - assert(in); - assert(name); - assert(error); - - /* - * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". - * If errno is omitted, then error is set to -1. - * Empty syscall name is not allowed. - * Here, we do not check that the syscall name is valid or not. - */ - - p = strchr(in, ':'); - if (p) { - e = seccomp_parse_errno_or_action(p + 1); - if (e < 0) - return e; - - n = strndup(in, p - in); - } else - n = strdup(in); - - if (!n) - return -ENOMEM; - - if (isempty(n)) - return -EINVAL; - - *error = e; - *name = TAKE_PTR(n); - - return 0; -} -#endif - static const char *mangle_base(const char *s, unsigned *base) { const char *k; @@ -636,14 +593,13 @@ int safe_atod(const char *s, double *ret_d) { } int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { - size_t i; unsigned val = 0; const char *s; s = *p; /* accept any number of digits, strtoull is limited to 19 */ - for (i=0; i < digits; i++,s++) { + for (size_t i = 0; i < digits; i++,s++) { if (*s < '0' || *s > '9') { if (i == 0) return -EINVAL; @@ -671,87 +627,6 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { return 0; } -int parse_percent_unbounded(const char *p) { - const char *pc, *n; - int r, v; - - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - - return v; -} - -int parse_percent(const char *p) { - int v; - - v = parse_percent_unbounded(p); - if (v > 100) - return -ERANGE; - - return v; -} - -int parse_permille_unbounded(const char *p) { - const char *pc, *pm, *dot, *n; - int r, q, v; - - pm = endswith(p, "‰"); - if (pm) { - n = strndupa(p, pm - p); - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - } else { - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - dot = memchr(p, '.', pc - p); - if (dot) { - if (dot + 2 != pc) - return -EINVAL; - if (dot[1] < '0' || dot[1] > '9') - return -EINVAL; - q = dot[1] - '0'; - n = strndupa(p, dot - p); - } else { - q = 0; - n = strndupa(p, pc - p); - } - r = safe_atoi(n, &v); - if (r < 0) - return r; - if (v < 0) - return -ERANGE; - if (v > (INT_MAX - q) / 10) - return -ERANGE; - - v = v * 10 + q; - } - - return v; -} - -int parse_permille(const char *p) { - int v; - - v = parse_permille_unbounded(p); - if (v > 1000) - return -ERANGE; - - return v; -} - int parse_nice(const char *p, int *ret) { int n, r; diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index 81478ed059..908202dafd 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -22,9 +22,6 @@ int parse_mtu(int family, const char *s, uint32_t *ret); int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); -#if HAVE_SECCOMP -int parse_syscall_and_errno(const char *in, char **name, int *error); -#endif #define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) #define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) @@ -130,12 +127,6 @@ int safe_atod(const char *s, double *ret_d); int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); -int parse_percent_unbounded(const char *p); -int parse_percent(const char *p); - -int parse_permille_unbounded(const char *p); -int parse_permille(const char *p); - int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index dae8b7341a..50ba44492e 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -586,26 +586,25 @@ char* path_join_internal(const char *first, ...) { } static int check_x_access(const char *path, int *ret_fd) { - if (ret_fd) { - _cleanup_close_ int fd = -1; - int r; + _cleanup_close_ int fd = -1; + int r; - /* We need to use O_PATH because there may be executables for which we have only exec - * permissions, but not read (usually suid executables). */ - fd = open(path, O_PATH|O_CLOEXEC); - if (fd < 0) - return -errno; + /* We need to use O_PATH because there may be executables for which we have only exec + * permissions, but not read (usually suid executables). */ + fd = open(path, O_PATH|O_CLOEXEC); + if (fd < 0) + return -errno; - r = access_fd(fd, X_OK); - if (r < 0) - return r; + r = fd_verify_regular(fd); + if (r < 0) + return r; + r = access_fd(fd, X_OK); + if (r < 0) + return r; + + if (ret_fd) *ret_fd = TAKE_FD(fd); - } else { - /* Let's optimize things a bit by not opening the file if we don't need the fd. */ - if (access(path, X_OK) < 0) - return -errno; - } return 0; } @@ -663,32 +662,20 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file return -ENOMEM; r = check_x_access(j, ret_fd ? &fd : NULL); - if (r >= 0) { - _cleanup_free_ char *with_dash; - - with_dash = strjoin(j, "/"); - if (!with_dash) - return -ENOMEM; - - /* If this passes, it must be a directory, and so should be skipped. */ - if (access(with_dash, X_OK) >= 0) - continue; - - /* We can't just `continue` inverting this case, since we need to update last_error. */ - if (errno == ENOTDIR) { - /* Found it! */ - if (ret_filename) - *ret_filename = path_simplify(TAKE_PTR(j), false); - if (ret_fd) - *ret_fd = TAKE_FD(fd); - - return 0; - } + if (r < 0) { + /* PATH entries which we don't have access to are ignored, as per tradition. */ + if (r != -EACCES) + last_error = r; + continue; } - /* PATH entries which we don't have access to are ignored, as per tradition. */ - if (errno != EACCES) - last_error = -errno; + /* Found it! */ + if (ret_filename) + *ret_filename = path_simplify(TAKE_PTR(j), false); + if (ret_fd) + *ret_fd = TAKE_FD(fd); + + return 0; } return last_error; @@ -765,38 +752,6 @@ int fsck_exists(const char *fstype) { return executable_is_good(checker); } -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { - char *p; - int r; - - /* - * This function is intended to be used in command line - * parsers, to handle paths that are passed in. It makes the - * path absolute, and reduces it to NULL if omitted or - * root (the latter optionally). - * - * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON - * SUCCESS! Hence, do not pass in uninitialized pointers. - */ - - if (isempty(path)) { - *arg = mfree(*arg); - return 0; - } - - r = path_make_absolute_cwd(path, &p); - if (r < 0) - return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); - - path_simplify(p, false); - if (suppress_root && empty_or_root(p)) - p = mfree(p); - - free_and_replace(*arg, p); - - return 0; -} - char* dirname_malloc(const char *path) { char *d, *dir, *dir2; @@ -836,6 +791,8 @@ const char *last_path_component(const char *path) { * Also, the empty string is mapped to itself. * * This is different than basename(), which returns "" when a trailing slash is present. + * + * This always succeeds (except if you pass NULL in which case it returns NULL, too). */ unsigned l, k; @@ -861,32 +818,84 @@ const char *last_path_component(const char *path) { int path_extract_filename(const char *p, char **ret) { _cleanup_free_ char *a = NULL; - const char *c, *e = NULL, *q; + const char *c; + size_t n; /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes - * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing slashes. */ + * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing + * slashes. Returns: + * + * -EINVAL → if the passed in path is not a valid path + * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir itself is specified + * -ENOMEM → no memory + * + * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to indicate + * the referenced file must be a directory. + * + * This function guarantees to return a fully valid filename, i.e. one that passes + * filename_is_valid() – this means "." and ".." are not accepted. */ - if (!p) + if (!path_is_valid(p)) return -EINVAL; + /* Special case the root dir, because in that case we simply have no filename, but + * last_path_component() won't complain */ + if (path_equal(p, "/")) + return -EADDRNOTAVAIL; + c = last_path_component(p); + n = strcspn(c, "/"); + + a = strndup(c, n); + if (!a) + return -ENOMEM; + + if (!filename_is_valid(a)) + return -EINVAL; + + *ret = TAKE_PTR(a); + return c[n] == '/' ? O_DIRECTORY : 0; +} + +int path_extract_directory(const char *p, char **ret) { + _cleanup_free_ char *a = NULL; + const char *c; - for (q = c; *q != 0; q++) - if (*q != '/') - e = q + 1; + /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns: + * + * -EINVAL → if the passed in path is not a valid path + * -EDESTADDRREQ → if no directory was specified in the passed in path, i.e. only a filename was passed + * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e. the root dir itself was specified + * -ENOMEM → no memory (surprise!) + * + * This function guarantees to return a fully valid path, i.e. one that passes path_is_valid(). + */ - if (!e) /* no valid character? */ + if (!path_is_valid(p)) return -EINVAL; - a = strndup(c, e - c); + /* Special case the root dir, because otherwise for an input of "///" last_path_component() returns + * the pointer to the last slash only, which might be seen as a valid path below. */ + if (path_equal(p, "/")) + return -EADDRNOTAVAIL; + + c = last_path_component(p); + + /* Delete trailing slashes, but keep one */ + while (c > p+1 && c[-1] == '/') + c--; + + if (p == c) /* No path whatsoever? Then return a recognizable error */ + return -EDESTADDRREQ; + + a = strndup(p, c - p); if (!a) return -ENOMEM; - if (!filename_is_valid(a)) + if (!path_is_valid(a)) return -EINVAL; *ret = TAKE_PTR(a); - return 0; } @@ -903,7 +912,7 @@ bool filename_is_valid(const char *p) { if (*e != 0) return false; - if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */ + if (e - p > NAME_MAX) /* NAME_MAX is counted *without* the trailing NUL byte */ return false; return true; @@ -914,10 +923,25 @@ bool path_is_valid(const char *p) { if (isempty(p)) return false; - if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */ - return false; + for (const char *e = p;;) { + size_t n; - return true; + /* Skip over slashes */ + e += strspn(e, "/"); + if (e - p >= PATH_MAX) /* Already reached the maximum length for a path? (PATH_MAX is counted + * *with* the trailing NUL byte) */ + return false; + if (*e == 0) /* End of string? Yay! */ + return true; + + /* Skip over one component */ + n = strcspn(e, "/"); + if (n > NAME_MAX) /* One component larger than NAME_MAX? (NAME_MAX is counted *without* the + * trailing NUL byte) */ + return false; + + e += n; + } } bool path_is_normalized(const char *p) { diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index e7a26c9a84..74ee6362ea 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -144,11 +144,10 @@ int fsck_exists(const char *fstype); _ret; \ }) -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); - char* dirname_malloc(const char *path); const char *last_path_component(const char *path); int path_extract_filename(const char *p, char **ret); +int path_extract_directory(const char *p, char **ret); bool filename_is_valid(const char *p) _pure_; bool path_is_valid(const char *p) _pure_; diff --git a/src/libnm-systemd-shared/src/basic/prioq.h b/src/libnm-systemd-shared/src/basic/prioq.h index 951576c021..7c76647611 100644 --- a/src/libnm-systemd-shared/src/basic/prioq.h +++ b/src/libnm-systemd-shared/src/basic/prioq.h @@ -8,7 +8,7 @@ typedef struct Prioq Prioq; -#define PRIOQ_IDX_NULL ((unsigned) -1) +#define PRIOQ_IDX_NULL (UINT_MAX) Prioq *prioq_new(compare_func_t compare); Prioq *prioq_free(Prioq *q); diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 0851613fc9..7d4301eadb 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -124,14 +124,10 @@ int get_process_comm(pid_t pid, char **ret) { } int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) { - _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *t = NULL, *ans = NULL; const char *p; - int r; size_t k; - - /* This is supposed to be a safety guard against runaway command lines. */ - size_t max_length = sc_arg_max(); + int r; assert(line); assert(pid >= 0); @@ -147,36 +143,18 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags * comm_fallback is false). Returns 0 and sets *line otherwise. */ p = procfs_file_alloca(pid, "cmdline"); - r = fopen_unlocked(p, "re", &f); + r = read_full_virtual_file(p, &t, &k); if (r == -ENOENT) return -ESRCH; if (r < 0) return r; - /* We assume that each four-byte character uses one or two columns. If we ever check for combining - * characters, this assumption will need to be adjusted. */ - if ((size_t) 4 * max_columns + 1 < max_columns) - max_length = MIN(max_length, (size_t) 4 * max_columns + 1); - - t = new(char, max_length); - if (!t) - return -ENOMEM; - - k = fread(t, 1, max_length, f); if (k > 0) { /* Arguments are separated by NULs. Let's replace those with spaces. */ for (size_t i = 0; i < k - 1; i++) if (t[i] == '\0') t[i] = ' '; - - t[k] = '\0'; /* Normally, t[k] is already NUL, so this is just a guard in case of short read */ } else { - /* We only treat getting nothing as an error. We *could* also get an error after reading some - * data, but we ignore that case, as such an error is rather unlikely and we prefer to get - * some data rather than none. */ - if (ferror(f)) - return -errno; - if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK)) return -ENOENT; @@ -187,7 +165,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags if (r < 0) return r; - mfree(t); + free(t); t = strjoin("[", t2, "]"); if (!t) return -ENOMEM; @@ -756,7 +734,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { /* Drop into a sigtimewait-based timeout. Waiting for the * pid to exit. */ - until = now(CLOCK_MONOTONIC) + timeout; + until = usec_add(now(CLOCK_MONOTONIC), timeout); for (;;) { usec_t n; siginfo_t status = {}; @@ -1230,6 +1208,11 @@ int safe_fork_full( original_pid = getpid_cached(); + if (flags & FORK_FLUSH_STDIO) { + fflush(stdout); + fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */ + } + if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can * be sure that SIGTERMs are not lost we might send to the child. */ @@ -1462,7 +1445,11 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret /* 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, ret_pid); + 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) @@ -1543,7 +1530,7 @@ int pidfd_get_pid(int fd, pid_t *ret) { xsprintf(path, "/proc/self/fdinfo/%i", fd); - r = read_full_file(path, &fdinfo, NULL); + r = read_full_virtual_file(path, &fdinfo, NULL); if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */ return -ESRCH; if (r < 0) @@ -1620,6 +1607,16 @@ int setpriority_closest(int priority) { return 0; } +bool invoked_as(char *argv[], const char *token) { + if (!argv || isempty(argv[0])) + return false; + + if (isempty(token)) + return false; + + return strstr(last_path_component(argv[0]), token); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index 6144f142c4..ddce7bd272 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -162,6 +162,7 @@ typedef enum ForkFlags { FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */ FORK_RLIMIT_NOFILE_SAFE = 1 << 10, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ FORK_STDOUT_TO_STDERR = 1 << 11, /* Make stdout a copy of stderr */ + FORK_FLUSH_STDIO = 1 << 12, /* fflush() stdout (and stderr) before forking */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); @@ -199,3 +200,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX); int pidfd_get_pid(int fd, pid_t *ret); int setpriority_closest(int priority); + +bool invoked_as(char *argv[], const char *token); diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index c831f06dac..a125e5228e 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -494,3 +494,22 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { return 1; } + +uint64_t random_u64_range(uint64_t m) { + uint64_t x, remainder; + + /* Generates a random number in the range 0…m-1, unbiased. (Java's algorithm) */ + + if (m == 0) /* Let's take m == 0 as special case to return an integer from the full range */ + return random_u64(); + if (m == 1) + return 0; + + remainder = UINT64_MAX % m; + + do { + x = random_u64(); + } while (x >= UINT64_MAX - remainder); + + return x % m; +} diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h index f661fc093a..e6528ddc7f 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.h +++ b/src/libnm-systemd-shared/src/basic/random-util.h @@ -40,3 +40,5 @@ int rdrand(unsigned long *ret); size_t random_pool_size(void); int random_write_entropy(int fd, const void *seed, size_t size, bool credit); + +uint64_t random_u64_range(uint64_t max); diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.c b/src/libnm-systemd-shared/src/basic/ratelimit.c index bae2ec3ffc..005bf31dc7 100644 --- a/src/libnm-systemd-shared/src/basic/ratelimit.c +++ b/src/libnm-systemd-shared/src/basic/ratelimit.c @@ -19,7 +19,7 @@ bool ratelimit_below(RateLimit *r) { ts = now(CLOCK_MONOTONIC); if (r->begin <= 0 || - ts - r->begin > r->interval) { + usec_sub_unsigned(ts, r->begin) > r->interval) { r->begin = ts; /* Reset counter */ diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h index 57ff713039..52b6f4984c 100644 --- a/src/libnm-systemd-shared/src/basic/set.h +++ b/src/libnm-systemd-shared/src/basic/set.h @@ -152,3 +152,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); #define _cleanup_set_free_free_ _cleanup_(set_free_freep) int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret); + +bool set_equal(Set *a, Set *b); diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c index 63b833b218..131fd3ba00 100644 --- a/src/libnm-systemd-shared/src/basic/signal-util.c +++ b/src/libnm-systemd-shared/src/basic/signal-util.c @@ -15,9 +15,9 @@ int reset_all_signal_handlers(void) { .sa_handler = SIG_DFL, .sa_flags = SA_RESTART, }; - int sig, r = 0; + int r = 0; - for (sig = 1; sig < _NSIG; sig++) { + for (int sig = 1; sig < _NSIG; sig++) { /* These two cannot be caught... */ if (IN_SET(sig, SIGKILL, SIGSTOP)) @@ -45,11 +45,14 @@ int reset_signal_mask(void) { return 0; } -static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { - int r = 0; +int sigaction_many_internal(const struct sigaction *sa, ...) { + int sig, r = 0; + va_list ap; + + va_start(ap, sa); /* negative signal ends the list. 0 signal is skipped. */ - for (; sig >= 0; sig = va_arg(ap, int)) { + while ((sig = va_arg(ap, int)) >= 0) { if (sig == 0) continue; @@ -60,49 +63,6 @@ static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { } } - return r; -} - -int sigaction_many(const struct sigaction *sa, ...) { - va_list ap; - int r; - - va_start(ap, sa); - r = sigaction_many_ap(sa, 0, ap); - va_end(ap); - - return r; -} - -int ignore_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -int default_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); va_end(ap); return r; @@ -198,7 +158,7 @@ static const char *const __signal_table[] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); const char *signal_to_string(int signo) { - static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int) + 1]; + static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)]; const char *name; name = __signal_to_string(signo); diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h index bdd39d429d..37271d7a68 100644 --- a/src/libnm-systemd-shared/src/basic/signal-util.h +++ b/src/libnm-systemd-shared/src/basic/signal-util.h @@ -8,9 +8,28 @@ int reset_all_signal_handlers(void); int reset_signal_mask(void); -int ignore_signals(int sig, ...); -int default_signals(int sig, ...); -int sigaction_many(const struct sigaction *sa, ...); +int sigaction_many_internal(const struct sigaction *sa, ...); + +#define ignore_signals(...) \ + sigaction_many_internal( \ + &(const struct sigaction) { \ + .sa_handler = SIG_IGN, \ + .sa_flags = SA_RESTART \ + }, \ + __VA_ARGS__, \ + -1) + +#define default_signals(...) \ + sigaction_many_internal( \ + &(const struct sigaction) { \ + .sa_handler = SIG_DFL, \ + .sa_flags = SA_RESTART \ + }, \ + __VA_ARGS__, \ + -1) + +#define sigaction_many(sa, ...) \ + sigaction_many_internal(sa, __VA_ARGS__, -1) int sigset_add_many(sigset_t *ss, ...); int sigprocmask_many(int how, sigset_t *old, ...); diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c index 59039bea4f..552ec053ff 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.c +++ b/src/libnm-systemd-shared/src/basic/socket-util.c @@ -31,6 +31,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "sysctl-util.h" #include "user-util.h" #include "utf8.h" @@ -277,10 +278,48 @@ const char* socket_address_get_path(const SocketAddress *a) { } bool socket_ipv6_is_supported(void) { - if (access("/proc/net/if_inet6", F_OK) != 0) + static int cached = -1; + + if (cached < 0) { + + if (access("/proc/net/if_inet6", F_OK) < 0) { + + if (errno != ENOENT) { + log_debug_errno(errno, "Unexpected error when checking whether /proc/net/if_inet6 exists: %m"); + return false; + } + + cached = false; + } else + cached = true; + } + + return cached; +} + +bool socket_ipv6_is_enabled(void) { + _cleanup_free_ char *v = NULL; + int r; + + /* Much like socket_ipv6_is_supported(), but also checks that the sysctl that disables IPv6 on all + * interfaces isn't turned on */ + + if (!socket_ipv6_is_supported()) return false; - return true; + r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &v); + if (r < 0) { + log_debug_errno(r, "Unexpected error reading 'net.ipv6.conf.all.disable_ipv6' sysctl: %m"); + return true; + } + + r = parse_boolean(v); + if (r < 0) { + log_debug_errno(r, "Failed to pare 'net.ipv6.conf.all.disable_ipv6' sysctl: %m"); + return true; + } + + return !r; } bool socket_address_matches_fd(const SocketAddress *a, int fd) { @@ -721,6 +760,10 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { if (isempty(p)) return false; + /* A valid ifindex? If so, it's valid iff IFNAME_VALID_NUMERIC is set */ + if (parse_ifindex(p) >= 0) + return flags & IFNAME_VALID_NUMERIC; + if (flags & IFNAME_VALID_ALTERNATIVE) { if (strlen(p) >= ALTIFNAMSIZ) return false; @@ -732,6 +775,11 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { if (dot_or_dot_dot(p)) return false; + /* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl + * directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */ + if (STR_IN_SET(p, "all", "default")) + return false; + for (const char *t = p; *t; t++) { if ((unsigned char) *t >= 127U) return false; @@ -739,20 +787,19 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { if ((unsigned char) *t <= 32U) return false; - if (IN_SET(*t, ':', '/')) + 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 */ return false; numeric = numeric && (*t >= '0' && *t <= '9'); } - if (numeric) { - if (!(flags & IFNAME_VALID_NUMERIC)) - return false; - - /* Verify that the number is well-formatted and in range. */ - if (parse_ifindex(p) < 0) - return false; - } + /* It's fully numeric but didn't parse as valid ifindex above? if so, it must be too large or zero or + * so, let's refuse that. */ + if (numeric) + return false; return true; } diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index 240d209c14..507a599d7c 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -63,7 +63,7 @@ typedef enum SocketAddressBindIPv6Only { SOCKET_ADDRESS_BOTH, SOCKET_ADDRESS_IPV6_ONLY, _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 + _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -EINVAL, } SocketAddressBindIPv6Only; #define socket_address_family(a) ((a)->sockaddr.sa.sa_family) @@ -101,6 +101,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_ const char* socket_address_get_path(const SocketAddress *a); bool socket_ipv6_is_supported(void); +bool socket_ipv6_is_enabled(void); int sockaddr_port(const struct sockaddr *_sa, unsigned *port); const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa); @@ -133,9 +134,9 @@ int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); typedef enum { - IFNAME_VALID_ALTERNATIVE = 1 << 0, - IFNAME_VALID_NUMERIC = 1 << 1, - _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC, + IFNAME_VALID_ALTERNATIVE = 1 << 0, + IFNAME_VALID_NUMERIC = 1 << 1, + _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC, } IfnameValidFlags; bool ifname_valid_full(const char *p, IfnameValidFlags flags); static inline bool ifname_valid(const char *p) { diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index f999681636..72a7e4a48b 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -72,12 +72,17 @@ int dir_is_empty_at(int dir_fd, const char *path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - if (path) + if (path) { fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - else - fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (fd < 0) - return -errno; + if (fd < 0) + return -errno; + } else { + /* Note that DUPing is not enough, as the internal pointer + * would still be shared and moved by FOREACH_DIRENT. */ + fd = fd_reopen(dir_fd, O_CLOEXEC); + if (fd < 0) + return fd; + } d = take_fdopendir(&fd); if (!d) diff --git a/src/libnm-systemd-shared/src/basic/string-table.c b/src/libnm-systemd-shared/src/basic/string-table.c index 116021df82..3a6376714a 100644 --- a/src/libnm-systemd-shared/src/basic/string-table.c +++ b/src/libnm-systemd-shared/src/basic/string-table.c @@ -5,11 +5,11 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { if (!key) - return -1; + return -EINVAL; for (size_t i = 0; i < len; ++i) if (streq_ptr(table[i], key)) return (ssize_t) i; - return -1; + return -EINVAL; } diff --git a/src/libnm-systemd-shared/src/basic/string-table.h b/src/libnm-systemd-shared/src/basic/string-table.h index ae4ea145d3..97c1adc07d 100644 --- a/src/libnm-systemd-shared/src/basic/string-table.h +++ b/src/libnm-systemd-shared/src/basic/string-table.h @@ -29,7 +29,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ scope type name##_from_string(const char *s) { \ if (!s) \ - return -1; \ + return -EINVAL; \ int b = parse_boolean(s); \ if (b == 0) \ return (type) 0; \ @@ -60,14 +60,16 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k unsigned u = 0; \ type i; \ if (!s) \ - return (type) -1; \ + return -EINVAL; \ i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ if (i >= 0) \ return i; \ - if (safe_atou(s, &u) >= 0 && u <= max) \ - return (type) u; \ - return (type) -1; \ - } \ + if (safe_atou(s, &u) < 0) \ + return -EINVAL; \ + if (u > max) \ + return -EINVAL; \ + return (type) u; \ + } #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ @@ -79,12 +81,15 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) #define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,) +#define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) #define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) #define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,static) /* For string conversions where numbers are also acceptable */ #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ @@ -98,9 +103,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define DUMP_STRING_TABLE(name,type,max) \ do { \ - type _k; \ flockfile(stdout); \ - for (_k = 0; _k < (max); _k++) { \ + for (type _k = 0; _k < (max); _k++) { \ const char *_t; \ _t = name##_to_string(_k); \ if (!_t) \ diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index be42d5c4f5..058eec54ff 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -20,64 +20,6 @@ #include "utf8.h" #include "util.h" -int strcmp_ptr(const char *a, const char *b) { - /* Like strcmp(), but tries to make sense of NULL pointers */ - - if (a && b) - return strcmp(a, b); - return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ -} - -int strcasecmp_ptr(const char *a, const char *b) { - /* Like strcasecmp(), but tries to make sense of NULL pointers */ - - if (a && b) - return strcasecmp(a, b); - return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */ -} - -char* endswith(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (memcmp(s + sl - pl, postfix, pl) != 0) - return NULL; - - return (char*) s + sl - pl; -} - -char* endswith_no_case(const char *s, const char *postfix) { - size_t sl, pl; - - assert(s); - assert(postfix); - - sl = strlen(s); - pl = strlen(postfix); - - if (pl == 0) - return (char*) s + sl; - - if (sl < pl) - return NULL; - - if (strcasecmp(s + sl - pl, postfix) != 0) - return NULL; - - return (char*) s + sl - pl; -} - char* first_word(const char *s, const char *word) { size_t sl, wl; const char *p; @@ -129,7 +71,7 @@ char *strnappend(const char *s, const char *suffix, size_t b) { assert(suffix); a = strlen(s); - if (b > ((size_t) -1) - a) + if (b > SIZE_MAX - a) return NULL; r = new(char, a+b+1); @@ -365,7 +307,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le assert(s); assert(percent <= 100); - assert(new_length != (size_t) -1); + assert(new_length != SIZE_MAX); if (old_length <= new_length) return strndup(s, old_length); @@ -436,7 +378,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne assert(s); assert(percent <= 100); - if (new_length == (size_t) -1) + if (new_length == SIZE_MAX) return strndup(s, old_length); if (new_length == 0) @@ -862,9 +804,8 @@ char *strextend_with_separator_internal(char **x, const char *separator, ...) { } char *strrep(const char *s, unsigned n) { - size_t l; char *r, *p; - unsigned i; + size_t l; assert(s); @@ -873,7 +814,7 @@ char *strrep(const char *s, unsigned n) { if (!r) return NULL; - for (i = 0; i < n; i++) + for (unsigned i = 0; i < n; i++) p = stpcpy(p, s); *p = 0; diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 593cf04ae1..cb2881b64d 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "macro.h" +#include "string-util-fundamental.h" /* What is interpreted as whitespace? */ #define WHITESPACE " \t\n\r" @@ -21,18 +22,6 @@ #define ALPHANUMERICAL LETTERS DIGITS #define HEXDIGITS DIGITS "abcdefABCDEF" -#define streq(a,b) (strcmp((a),(b)) == 0) -#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) -#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) -#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) - -int strcmp_ptr(const char *a, const char *b) _pure_; -int strcasecmp_ptr(const char *a, const char *b) _pure_; - -static inline bool streq_ptr(const char *a, const char *b) { - return strcmp_ptr(a, b) == 0; -} - static inline char* strstr_ptr(const char *haystack, const char *needle) { if (!haystack || !needle) return NULL; @@ -51,10 +40,6 @@ static inline const char *strna(const char *s) { return s ?: "n/a"; } -static inline const char* yes_no(bool b) { - return b ? "yes" : "no"; -} - static inline const char* true_false(bool b) { return b ? "true" : "false"; } @@ -71,10 +56,6 @@ static inline const char* enable_disable(bool b) { return b ? "enable" : "disable"; } -static inline bool isempty(const char *p) { - return !p || !p[0]; -} - static inline const char *empty_to_null(const char *p) { return isempty(p) ? NULL : p; } @@ -93,29 +74,6 @@ static inline const char *empty_or_dash_to_null(const char *p) { return empty_or_dash(p) ? NULL : p; } -static inline char *startswith(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -static inline char *startswith_no_case(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncasecmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -char *endswith(const char *s, const char *postfix) _pure_; -char *endswith_no_case(const char *s, const char *postfix) _pure_; - char *first_word(const char *s, const char *word) _pure_; char *strnappend(const char *s, const char *suffix, size_t length); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index 492dfe4002..765da04a7b 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -240,27 +240,28 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { return 0; } -char **strv_split_newlines(const char *s) { - char **l; +int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags) { + _cleanup_strv_free_ char **l = NULL; size_t n; + int r; assert(s); - /* Special version of strv_split() that splits on newlines and - * suppresses an empty string at the end */ + /* Special version of strv_split_full() that splits on newlines and + * suppresses an empty string at the end. */ - l = strv_split(s, NEWLINE); - if (!l) - return NULL; + r = strv_split_full(&l, s, NEWLINE, flags); + if (r < 0) + return r; n = strv_length(l); - if (n <= 0) - return l; - - if (isempty(l[n - 1])) + if (n > 0 && isempty(l[n - 1])) { l[n - 1] = mfree(l[n - 1]); + n--; + } - return l; + *ret = TAKE_PTR(l); + return n; } int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) { diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 6b3e8e7f86..911528fab4 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -73,15 +73,21 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split_newlines(const char *s); - int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); static inline char **strv_split(const char *s, const char *separators) { char **ret; - int r; - r = strv_split_full(&ret, s, separators, 0); - if (r < 0) + if (strv_split_full(&ret, s, separators, 0) < 0) + return NULL; + + return ret; +} + +int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags); +static inline char **strv_split_newlines(const char *s) { + char **ret; + + if (strv_split_newlines_full(&ret, s, 0) < 0) return NULL; return ret; diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index 52c564a68d..3c2b25bd2a 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -493,7 +493,6 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { { "us", 1 }, }; - size_t i; char *p = buf; bool something = false; @@ -514,7 +513,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { /* The result of this function can be parsed with parse_sec */ - for (i = 0; i < ELEMENTSOF(table); i++) { + for (size_t i = 0; i < ELEMENTSOF(table); i++) { int k = 0; size_t n; bool done = false; @@ -962,9 +961,8 @@ static const char* extract_multiplier(const char *p, usec_t *multiplier) { { "us", 1ULL }, { "µs", 1ULL }, }; - size_t i; - for (i = 0; i < ELEMENTSOF(table); i++) { + for (size_t i = 0; i < ELEMENTSOF(table); i++) { char *e; e = startswith(p, table[i].suffix); diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index 89ee8b4a96..d716074fbe 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -35,7 +35,7 @@ typedef enum TimestampStyle { TIMESTAMP_UTC, TIMESTAMP_US_UTC, _TIMESTAMP_STYLE_MAX, - _TIMESTAMP_STYLE_INVALID = -1, + _TIMESTAMP_STYLE_INVALID = -EINVAL, } TimestampStyle; #define USEC_INFINITY ((usec_t) UINT64_MAX) @@ -155,16 +155,14 @@ 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) { - usec_t c; /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't * overflow. */ - c = a + b; - if (c < a || c < b) /* overflow check */ + if (a > USEC_INFINITY - b) /* overflow check */ return USEC_INFINITY; - return c; + return a + b; } static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 49c343773c..5ee71d0158 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -94,16 +94,11 @@ int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { } int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { - const char *fn; - char *t; + _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL; + int r; assert(ret); - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - /* * Turns this: * /foo/bar/waldo @@ -112,35 +107,41 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { * /foo/bar/.#<extra>waldoXXXXXX */ - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; + r = path_extract_directory(p, &d); + if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */ + return r; - extra = strempty(extra); + r = path_extract_filename(p, &fn); + if (r < 0) + return r; - t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); - if (!t) + nf = strjoin(".#", strempty(extra), fn, "XXXXXX"); + if (!nf) return -ENOMEM; - strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX"); + if (!filename_is_valid(nf)) /* New name is not valid? (Maybe because too long?) Refuse. */ + return -EINVAL; + + if (d) { + char *j; + + j = path_join(d, nf); + if (!j) + return -ENOMEM; + + *ret = path_simplify(j, false); + } else + *ret = TAKE_PTR(nf); - *ret = path_simplify(t, false); return 0; } int tempfn_random(const char *p, const char *extra, char **ret) { - const char *fn; - char *t, *x; - uint64_t u; - unsigned i; + _cleanup_free_ char *d = NULL, *fn = NULL, *nf = NULL; + int r; assert(ret); - if (isempty(p)) - return -EINVAL; - if (path_equal(p, "/")) - return -EINVAL; - /* * Turns this: * /foo/bar/waldo @@ -149,34 +150,40 @@ int tempfn_random(const char *p, const char *extra, char **ret) { * /foo/bar/.#<extra>waldobaa2a261115984a9 */ - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; + r = path_extract_directory(p, &d); + if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */ + return r; - extra = strempty(extra); + r = path_extract_filename(p, &fn); + if (r < 0) + return r; - t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); - if (!t) + if (asprintf(&nf, ".#%s%s%016" PRIx64, + strempty(extra), + fn, + random_u64()) < 0) return -ENOMEM; - x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn); + if (!filename_is_valid(nf)) /* Not valid? (maybe because too long now?) — refuse early */ + return -EINVAL; - u = random_u64(); - for (i = 0; i < 16; i++) { - *(x++) = hexchar(u & 0xF); - u >>= 4; - } + if (d) { + char *j; - *x = 0; + j = path_join(d, nf); + if (!j) + return -ENOMEM; + + *ret = path_simplify(j, false); + } else + *ret = TAKE_PTR(nf); - *ret = path_simplify(t, false); return 0; } int tempfn_random_child(const char *p, const char *extra, char **ret) { char *t, *x; uint64_t u; - unsigned i; int r; assert(ret); @@ -205,7 +212,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra); u = random_u64(); - for (i = 0; i < 16; i++) { + for (unsigned i = 0; i < 16; i++) { *(x++) = hexchar(u & 0xF); u >>= 4; } diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c index 59663c0350..46c3a463b9 100644 --- a/src/libnm-systemd-shared/src/basic/utf8.c +++ b/src/libnm-systemd-shared/src/basic/utf8.c @@ -81,7 +81,7 @@ static size_t utf8_encoded_expected_len(uint8_t c) { /* decode one unicode char */ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { char32_t unichar; - size_t len, i; + size_t len; assert(str); @@ -110,7 +110,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { return -EINVAL; } - for (i = 1; i < len; i++) { + for (size_t i = 1; i < len; i++) { if (((char32_t)str[i] & 0xc0) != 0x80) return -EINVAL; @@ -156,14 +156,14 @@ char *utf8_is_valid_n(const char *str, size_t len_bytes) { assert(str); - for (const char *p = str; len_bytes != (size_t) -1 ? (size_t) (p - str) < len_bytes : *p != '\0'; ) { + for (const char *p = str; len_bytes != SIZE_MAX ? (size_t) (p - str) < len_bytes : *p != '\0'; ) { int len; - if (_unlikely_(*p == '\0') && len_bytes != (size_t) -1) + if (_unlikely_(*p == '\0') && len_bytes != SIZE_MAX) return NULL; /* embedded NUL */ len = utf8_encoded_valid_unichar(p, - len_bytes != (size_t) -1 ? len_bytes - (p - str) : (size_t) -1); + len_bytes != SIZE_MAX ? len_bytes - (p - str) : SIZE_MAX); if (_unlikely_(len < 0)) return NULL; /* invalid character */ @@ -185,7 +185,7 @@ char *utf8_escape_invalid(const char *str) { while (*str) { int len; - len = utf8_encoded_valid_unichar(str, (size_t) -1); + len = utf8_encoded_valid_unichar(str, SIZE_MAX); if (len > 0) { s = mempcpy(s, str, len); str += len; @@ -233,7 +233,7 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) { if (!*str) /* done! */ goto finish; - len = utf8_encoded_valid_unichar(str, (size_t) -1); + len = utf8_encoded_valid_unichar(str, SIZE_MAX); if (len > 0) { if (utf8_is_printable(str, len)) { int w; @@ -302,14 +302,12 @@ char *ascii_is_valid(const char *str) { } char *ascii_is_valid_n(const char *str, size_t len) { - size_t i; - /* Very similar to ascii_is_valid(), but checks exactly len * bytes and rejects any NULs in that range. */ assert(str); - for (i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) if ((unsigned char) str[i] >= 128 || str[i] == 0) return NULL; @@ -436,7 +434,6 @@ size_t utf16_encode_unichar(char16_t *out, char32_t c) { char16_t *utf8_to_utf16(const char *s, size_t length) { char16_t *n, *p; - size_t i; int r; assert(s); @@ -447,7 +444,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length) { p = n; - for (i = 0; i < length;) { + for (size_t i = 0; i < length;) { char32_t unichar; size_t e; @@ -505,13 +502,13 @@ static int utf8_unichar_to_encoded_len(char32_t unichar) { /* validate one encoded unicode char and return its length */ int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { char32_t unichar; - size_t len, i; + size_t len; int r; assert(str); assert(length > 0); - /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */ + /* We read until NUL, at most length bytes. SIZE_MAX may be used to disable the length check. */ len = utf8_encoded_expected_len(str[0]); if (len == 0) @@ -526,7 +523,7 @@ int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { return 1; /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) if ((str[i] & 0x80) != 0x80) return -EINVAL; @@ -548,14 +545,14 @@ int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { size_t utf8_n_codepoints(const char *str) { size_t n = 0; - /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */ + /* Returns the number of UTF-8 codepoints in this string, or SIZE_MAX if the string is not valid UTF-8. */ while (*str != 0) { int k; - k = utf8_encoded_valid_unichar(str, (size_t) -1); + k = utf8_encoded_valid_unichar(str, SIZE_MAX); if (k < 0) - return (size_t) -1; + return SIZE_MAX; str += k; n++; @@ -575,7 +572,7 @@ size_t utf8_console_width(const char *str) { w = utf8_char_console_width(str); if (w < 0) - return (size_t) -1; + return SIZE_MAX; n += w; str = utf8_next_char(str); diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h index a6ea942c62..219ca89184 100644 --- a/src/libnm-systemd-shared/src/basic/utf8.h +++ b/src/libnm-systemd-shared/src/basic/utf8.h @@ -16,7 +16,7 @@ bool unichar_is_valid(char32_t c); char *utf8_is_valid_n(const char *str, size_t len_bytes) _pure_; static inline char *utf8_is_valid(const char *s) { - return utf8_is_valid_n(s, (size_t) -1); + return utf8_is_valid_n(s, SIZE_MAX); } char *ascii_is_valid(const char *s) _pure_; char *ascii_is_valid_n(const char *str, size_t len); @@ -27,7 +27,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin char *utf8_escape_invalid(const char *s); char *utf8_escape_non_printable_full(const char *str, size_t console_width); static inline char *utf8_escape_non_printable(const char *str) { - return utf8_escape_non_printable_full(str, (size_t) -1); + return utf8_escape_non_printable_full(str, SIZE_MAX); } size_t utf8_encode_unichar(char *out_utf8, char32_t g); diff --git a/src/libnm-systemd-shared/src/basic/util.c b/src/libnm-systemd-shared/src/basic/util.c index 7c708eb3be..955b18bd2a 100644 --- a/src/libnm-systemd-shared/src/basic/util.c +++ b/src/libnm-systemd-shared/src/basic/util.c @@ -52,13 +52,14 @@ int prot_from_flags(int flags) { } bool in_initrd(void) { - struct statfs s; int r; + const char *e; + bool lenient = false; if (saved_in_initrd >= 0) return saved_in_initrd; - /* We make two checks here: + /* We have two checks here: * * 1. the flag file /etc/initrd-release must exist * 2. the root file system must be a memory file system @@ -66,18 +67,46 @@ bool in_initrd(void) { * The second check is extra paranoia, since misdetecting an * initrd can have bad consequences due the initrd * emptying when transititioning to the main systemd. + * + * If env var $SYSTEMD_IN_INITRD is not set or set to "auto", + * both checks are used. If it's set to "lenient", only check + * 1 is used. If set to a boolean value, then the boolean + * value is returned. */ - r = getenv_bool_secure("SYSTEMD_IN_INITRD"); - if (r < 0 && r != -ENXIO) - log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); + e = secure_getenv("SYSTEMD_IN_INITRD"); + if (e) { + if (streq(e, "lenient")) + lenient = true; + else if (!streq(e, "auto")) { + r = parse_boolean(e); + if (r >= 0) { + saved_in_initrd = r > 0; + return saved_in_initrd; + } + log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); + } + } + + if (!lenient) { + r = path_is_temporary_fs("/"); + if (r < 0) + log_debug_errno(r, "Couldn't determine if / is a temporary file system: %m"); - if (r >= 0) saved_in_initrd = r > 0; - else - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); + } + + r = access("/etc/initrd-release", F_OK); + if (r >= 0) { + if (saved_in_initrd == 0) + log_debug("/etc/initrd-release exists, but it's not an initrd."); + else + saved_in_initrd = 1; + } else { + if (errno != ENOENT) + log_debug_errno(errno, "Failed to test if /etc/initrd-release exists: %m"); + saved_in_initrd = 0; + } return saved_in_initrd; } @@ -198,68 +227,6 @@ int version(void) { return 0; } -/* This is a direct translation of str_verscmp from boot.c */ -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int c_order(int c) { - if (c == 0 || is_digit(c)) - return 0; - - if ((c >= 'a') && (c <= 'z')) - return c; - - return c + 0x10000; -} - -int str_verscmp(const char *s1, const char *s2) { - const char *os1, *os2; - - assert(s1); - assert(s2); - - os1 = s1; - os2 = s2; - - while (*s1 || *s2) { - int first; - - while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { - int order; - - order = c_order(*s1) - c_order(*s2); - if (order != 0) - return order; - s1++; - s2++; - } - - while (*s1 == '0') - s1++; - while (*s2 == '0') - s2++; - - first = 0; - while (is_digit(*s1) && is_digit(*s2)) { - if (first == 0) - first = *s1 - *s2; - s1++; - s2++; - } - - if (is_digit(*s1)) - return 1; - if (is_digit(*s2)) - return -1; - - if (first != 0) - return first; - } - - return strcmp(os1, os2); -} - /* Turn off core dumps but only if we're running outside of a container. */ void disable_coredumps(void) { int r; diff --git a/src/libnm-systemd-shared/src/basic/util.h b/src/libnm-systemd-shared/src/basic/util.h index 942d773ff1..b6c51c036e 100644 --- a/src/libnm-systemd-shared/src/basic/util.h +++ b/src/libnm-systemd-shared/src/basic/util.h @@ -63,6 +63,4 @@ int container_get_leader(const char *machine, pid_t *pid); int version(void); -int str_verscmp(const char *s1, const char *s2); - void disable_coredumps(void); diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h new file mode 100644 index 0000000000..790920eb23 --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#ifndef SD_BOOT +#include <assert.h> +#endif + +#include "type.h" + +#define _const_ __attribute__((__const__)) +#define _pure_ __attribute__((__pure__)) +#define _unused_ __attribute__((__unused__)) +#define _cleanup_(x) __attribute__((__cleanup__(x))) + +#ifndef __COVERITY__ +# define VOID_0 ((void)0) +#else +# define VOID_0 ((void*)0) +#endif + +#define ELEMENTSOF(x) \ + (__builtin_choose_expr( \ + !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ + sizeof(x)/sizeof((x)[0]), \ + VOID_0)) + +#define XCONCATENATE(x, y) x ## y +#define CONCATENATE(x, y) XCONCATENATE(x, y) + +#ifdef SD_BOOT +#define assert(expr) do {} while (false) +#endif + +#if defined(static_assert) +#define assert_cc(expr) \ + static_assert(expr, #expr) +#else +#define assert_cc(expr) \ + struct CONCATENATE(_assert_struct_, __COUNTER__) { \ + char x[(expr) ? 0 : -1]; \ + } +#endif + +#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) +#define UNIQ __COUNTER__ + +#undef MAX +#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) +#define __MAX(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define CONST_MAX(_A, _B) \ + (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + ((_A) > (_B)) ? (_A) : (_B), \ + VOID_0)) + +/* takes two types and returns the size of the larger one */ +#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) + +#define MAX3(x, y, z) \ + ({ \ + const typeof(x) _c = MAX(x, y); \ + MAX(_c, z); \ + }) + +#define MAX4(x, y, z, a) \ + ({ \ + const typeof(x) _d = MAX3(x, y, z); \ + MAX(_d, a); \ + }) + +#undef MIN +#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) +#define __MIN(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ + }) + +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define CONST_MIN(_A, _B) \ + (__builtin_choose_expr( \ + __builtin_constant_p(_A) && \ + __builtin_constant_p(_B) && \ + __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + ((_A) < (_B)) ? (_A) : (_B), \ + VOID_0)) + +#define MIN3(x, y, z) \ + ({ \ + const typeof(x) _c = MIN(x, y); \ + MIN(_c, z); \ + }) + +#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) +#define __LESS_BY(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) - UNIQ_T(B, bq) : 0; \ + }) + +#define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b)) +#define __CMP(aq, a, bq, b) \ + ({ \ + const typeof(a) UNIQ_T(A, aq) = (a); \ + const typeof(b) UNIQ_T(B, bq) = (b); \ + UNIQ_T(A, aq) < UNIQ_T(B, bq) ? -1 : \ + UNIQ_T(A, aq) > UNIQ_T(B, bq) ? 1 : 0; \ + }) + +#undef CLAMP +#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) +#define __CLAMP(xq, x, lowq, low, highq, high) \ + ({ \ + const typeof(x) UNIQ_T(X, xq) = (x); \ + const typeof(low) UNIQ_T(LOW, lowq) = (low); \ + const typeof(high) UNIQ_T(HIGH, highq) = (high); \ + UNIQ_T(X, xq) > UNIQ_T(HIGH, highq) ? \ + UNIQ_T(HIGH, highq) : \ + UNIQ_T(X, xq) < UNIQ_T(LOW, lowq) ? \ + UNIQ_T(LOW, lowq) : \ + UNIQ_T(X, xq); \ + }) + +/* [(x + y - 1) / y] suffers from an integer overflow, even though the + * computation should be possible in the given type. Therefore, we use + * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the + * quotient and the remainder, so both should be equally fast. */ +#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y)) +#define __DIV_ROUND_UP(xq, x, yq, y) \ + ({ \ + const typeof(x) UNIQ_T(X, xq) = (x); \ + const typeof(y) UNIQ_T(Y, yq) = (y); \ + (UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \ + }) + +#define CASE_F(X) case X: +#define CASE_F_1(CASE, X) CASE_F(X) +#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) +#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) +#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) +#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) +#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) +#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) +#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) +#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) +#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) +#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) +#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) +#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) +#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) +#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) +#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) +#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) +#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) +#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) +#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) + +#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME +#define FOR_EACH_MAKE_CASE(...) \ + GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ + CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ + (CASE_F,__VA_ARGS__) + +#define IN_SET(x, ...) \ + ({ \ + sd_bool _found = false; \ + /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ + * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ + * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ + * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ + static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \ + assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ + switch(x) { \ + FOR_EACH_MAKE_CASE(__VA_ARGS__) \ + _found = true; \ + break; \ + default: \ + break; \ + } \ + _found; \ + }) + +/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time + * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ +#define TAKE_PTR(ptr) \ + ({ \ + typeof(ptr) _ptr_ = (ptr); \ + (ptr) = NULL; \ + _ptr_; \ + }) diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c new file mode 100644 index 0000000000..9f14597fef --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef SD_BOOT +#include <ctype.h> + +#include "macro.h" +#endif +#include "string-util-fundamental.h" + +sd_char *startswith(const sd_char *s, const sd_char *prefix) { + sd_size_t l; + + assert(s); + assert(prefix); + + l = strlen(prefix); + if (!strneq(s, prefix, l)) + return NULL; + + return (sd_char*) s + l; +} + +#ifndef SD_BOOT +sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) { + sd_size_t l; + + assert(s); + assert(prefix); + + l = strlen(prefix); + if (!strncaseeq(s, prefix, l)) + return NULL; + + return (sd_char*) s + l; +} +#endif + +sd_char* endswith(const sd_char *s, const sd_char *postfix) { + sd_size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (sd_char*) s + sl; + + if (sl < pl) + return NULL; + + if (strcmp(s + sl - pl, postfix) != 0) + return NULL; + + return (sd_char*) s + sl - pl; +} + +sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) { + sd_size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return (sd_char*) s + sl; + + if (sl < pl) + return NULL; + + if (strcasecmp(s + sl - pl, postfix) != 0) + return NULL; + + return (sd_char*) s + sl - pl; +} + +#ifdef SD_BOOT +static sd_bool isdigit(sd_char a) { + return a >= '0' && a <= '9'; +} +#endif + +static sd_bool is_alpha(sd_char a) { + /* Locale independent version of isalpha(). */ + return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); +} + +static sd_bool is_valid_version_char(sd_char a) { + return isdigit(a) || is_alpha(a) || IN_SET(a, '~', '-', '^', '.'); +} + +sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { + + /* This is based on RPM's rpmvercmp(). But this explicitly handles '-' and '.', as we usually + * want to directly compare strings which contain both version and release; e.g. + * '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'. + * Unlike rpmvercmp(), this distiguishes e.g. 123a and 123.a, and 123a is newer. + * + * This splits the input strings into segments. Each segment is numeric or alpha, and may be + * prefixed with the following: + * '~' : used for pre-releases, a segment prefixed with this is the oldest, + * '-' : used for the separator between version and release, + * '^' : used for patched releases, a segment with this is newer than one with '-'. + * '.' : used for point releases. + * Note, no prefix segment is the newest. All non-supported characters are dropped, and + * handled as a separator of segments, e.g., 123_a is equivalent to 123a. + * + * By using this, version strings can be sorted like following: + * (older) 122.1 + * ^ 123~rc1-1 + * | 123 + * | 123-a + * | 123-a.1 + * | 123-1 + * | 123-1.1 + * | 123^post1 + * | 123.a-1 + * | 123.1-1 + * v 123a-1 + * (newer) 124-1 + */ + + if (isempty(a) || isempty(b)) + return strcmp_ptr(a, b); + + for (;;) { + const sd_char *aa, *bb; + sd_int r; + + /* Drop leading invalid characters. */ + while (*a != '\0' && !is_valid_version_char(*a)) + a++; + while (*b != '\0' && !is_valid_version_char(*b)) + b++; + + /* Handle '~'. Used for pre-releases, e.g. 123~rc1, or 4.5~alpha1 */ + if (*a == '~' || *b == '~') { + /* The string prefixed with '~' is older. */ + r = CMP(*a != '~', *b != '~'); + if (r != 0) + return r; + + /* Now both strings are prefixed with '~'. Compare remaining strings. */ + a++; + b++; + } + + /* If at least one string reaches the end, then longer is newer. + * Note that except for '~' prefixed segments, a string has more segments is newer. + * So, this check must be after the '~' check. */ + if (*a == '\0' || *b == '\0') + return strcmp(a, b); + + /* Handle '-', which separates version and release, e.g 123.4-3.1.fc33.x86_64 */ + if (*a == '-' || *b == '-') { + /* The string prefixed with '-' is older (e.g., 123-9 vs 123.1-1) */ + r = CMP(*a != '-', *b != '-'); + if (r != 0) + return r; + + a++; + b++; + } + + /* Handle '^'. Used for patched release. */ + if (*a == '^' || *b == '^') { + r = CMP(*a != '^', *b != '^'); + if (r != 0) + return r; + + a++; + b++; + } + + /* Handle '.'. Used for point releases. */ + if (*a == '.' || *b == '.') { + r = CMP(*a != '.', *b != '.'); + if (r != 0) + return r; + + a++; + b++; + } + + if (isdigit(*a) || isdigit(*b)) { + /* Skip leading '0', to make 00123 equivalent to 123. */ + while (*a == '0') + a++; + while (*b == '0') + b++; + + /* Find the leading numeric segments. One may be an empty string. So, + * numeric segments are always newer than alpha segments. */ + for (aa = a; *aa != '\0' && isdigit(*aa); aa++) + ; + for (bb = b; *bb != '\0' && isdigit(*bb); bb++) + ; + + /* To compare numeric segments without parsing their values, first compare the + * lengths of the segments. Eg. 12345 vs 123, longer is newer. */ + r = CMP(aa - a, bb - b); + if (r != 0) + return r; + + /* Then, compare them as strings. */ + r = strncmp(a, b, aa - a); + if (r != 0) + return r; + } else { + /* Find the leading non-numeric segments. */ + for (aa = a; *aa != '\0' && is_alpha(*aa); aa++) + ; + for (bb = b; *bb != '\0' && is_alpha(*bb); bb++) + ; + + /* Note that the segments are usually not NUL-terminated. */ + r = strncmp(a, b, MIN(aa - a, bb - b)); + if (r != 0) + return r; + + /* Longer is newer, e.g. abc vs abcde. */ + r = CMP(aa - a, bb - b); + if (r != 0) + return r; + } + + /* The current segments are equivalent. Let's compare the next one. */ + a = aa; + b = bb; + } +} diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h new file mode 100644 index 0000000000..407cede48d --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#ifdef SD_BOOT +#include <efi.h> +#include <efilib.h> +#else +#include <string.h> +#endif + +#include "macro-fundamental.h" + +#ifdef SD_BOOT +#define strlen(a) StrLen((a)) +#define strcmp(a, b) StrCmp((a), (b)) +#define strncmp(a, b, n) StrnCmp((a), (b), (n)) +#define strcasecmp(a, b) StriCmp((a), (b)) +#define STR_C(str) (L ## str) +#else +#define STR_C(str) (str) +#endif + +#define streq(a,b) (strcmp((a),(b)) == 0) +#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) +#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) +#ifndef SD_BOOT +#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) +#endif + +static inline sd_int strcmp_ptr(const sd_char *a, const sd_char *b) { + if (a && b) + return strcmp(a, b); + + return CMP(a, b); +} + +static inline sd_int strcasecmp_ptr(const sd_char *a, const sd_char *b) { + if (a && b) + return strcasecmp(a, b); + + return CMP(a, b); +} + +static inline sd_bool streq_ptr(const sd_char *a, const sd_char *b) { + return strcmp_ptr(a, b) == 0; +} + +static inline sd_bool strcaseeq_ptr(const sd_char *a, const sd_char *b) { + return strcasecmp_ptr(a, b) == 0; +} + +sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_; +#ifndef SD_BOOT +sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_; +#endif +sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_; +sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_; + +static inline sd_bool isempty(const sd_char *a) { + return !a || a[0] == '\0'; +} + +static inline const sd_char *yes_no(sd_bool b) { + return b ? STR_C("yes") : STR_C("no"); +} + +sd_int strverscmp_improved(const sd_char *a, const sd_char *b); diff --git a/src/libnm-systemd-shared/src/fundamental/type.h b/src/libnm-systemd-shared/src/fundamental/type.h new file mode 100644 index 0000000000..f645d2de7f --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/type.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#ifdef SD_BOOT +#include <efi.h> + +typedef BOOLEAN sd_bool; +typedef CHAR16 sd_char; +typedef INTN sd_int; +typedef UINTN sd_size_t; + +#define true TRUE +#define false FALSE +#else +#include <stdbool.h> +#include <stdint.h> + +typedef bool sd_bool; +typedef char sd_char; +typedef int sd_int; +typedef size_t sd_size_t; +#endif diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h index 77f596294d..c25fcaacc2 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.h +++ b/src/libnm-systemd-shared/src/shared/dns-domain.h @@ -6,24 +6,10 @@ #include <stddef.h> #include <stdint.h> +#include "dns-def.h" #include "hashmap.h" #include "in-addr-util.h" -/* Length of a single label, with all escaping removed, excluding any trailing dot or NUL byte */ -#define DNS_LABEL_MAX 63 - -/* Worst case length of a single label, with all escaping applied and room for a trailing NUL byte. */ -#define DNS_LABEL_ESCAPED_MAX (DNS_LABEL_MAX*4+1) - -/* Maximum length of a full hostname, consisting of a series of unescaped labels, and no trailing dot or NUL byte */ -#define DNS_HOSTNAME_MAX 253 - -/* Maximum length of a full hostname, on the wire, including the final NUL byte */ -#define DNS_WIRE_FORMAT_HOSTNAME_MAX 255 - -/* Maximum number of labels per valid hostname */ -#define DNS_N_LABELS_MAX 127 - typedef enum DNSLabelFlags { DNS_LABEL_LDH = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */ DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */ |