diff options
91 files changed, 2156 insertions, 1707 deletions
diff --git a/src/libnm-std-aux/unaligned-fundamental.h b/src/libnm-std-aux/unaligned-fundamental.h new file mode 100644 index 0000000000..a4c810a5fc --- /dev/null +++ b/src/libnm-std-aux/unaligned-fundamental.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdint.h> + +static inline uint16_t unaligned_read_ne16(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; + + return u->x; +} + +static inline uint32_t unaligned_read_ne32(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; + + return u->x; +} + +static inline uint64_t unaligned_read_ne64(const void *_u) { + const struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; + + return u->x; +} + +static inline void unaligned_write_ne16(void *_u, uint16_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint16_t x; } *u = _u; + + u->x = a; +} + +static inline void unaligned_write_ne32(void *_u, uint32_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint32_t x; } *u = _u; + + u->x = a; +} + +static inline void unaligned_write_ne64(void *_u, uint64_t a) { + struct __attribute__((__packed__, __may_alias__)) { uint64_t x; } *u = _u; + + u->x = a; +} diff --git a/src/libnm-std-aux/unaligned.h b/src/libnm-std-aux/unaligned.h index 4100be0803..04580cfb63 100644 --- a/src/libnm-std-aux/unaligned.h +++ b/src/libnm-std-aux/unaligned.h @@ -4,6 +4,8 @@ #include <endian.h> #include <stdint.h> +#include "unaligned-fundamental.h" + /* BE */ static inline uint16_t unaligned_read_be16(const void *_u) { @@ -79,21 +81,3 @@ static inline void unaligned_write_le64(void *_u, uint64_t a) { u->x = le64toh(a); } - -#if __BYTE_ORDER == __BIG_ENDIAN -#define unaligned_read_ne16 unaligned_read_be16 -#define unaligned_read_ne32 unaligned_read_be32 -#define unaligned_read_ne64 unaligned_read_be64 - -#define unaligned_write_ne16 unaligned_write_be16 -#define unaligned_write_ne32 unaligned_write_be32 -#define unaligned_write_ne64 unaligned_write_be64 -#else -#define unaligned_read_ne16 unaligned_read_le16 -#define unaligned_read_ne32 unaligned_read_le32 -#define unaligned_read_ne64 unaligned_read_le64 - -#define unaligned_write_ne16 unaligned_write_le16 -#define unaligned_write_ne32 unaligned_write_le32 -#define unaligned_write_ne64 unaligned_write_le64 -#endif diff --git a/src/libnm-systemd-core/src/libsystemd-network/arp-util.c b/src/libnm-systemd-core/src/libsystemd-network/arp-util.c deleted file mode 100644 index 99a5f69b70..0000000000 --- a/src/libnm-systemd-core/src/libsystemd-network/arp-util.c +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/*** - Copyright © 2014 Axis Communications AB. All rights reserved. -***/ - -#include <arpa/inet.h> -#include <linux/filter.h> -#include <netinet/if_ether.h> - -#include "arp-util.h" -#include "ether-addr-util.h" -#include "fd-util.h" -#include "in-addr-util.h" -#include "unaligned.h" -#include "util.h" - -int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac) { - struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */ - BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hln)), /* A <- hardware address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct ether_addr), 1, 0), /* length == sizeof(ether_addr)? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pln)), /* A <- protocol address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(struct in_addr), 1, 0), /* length == sizeof(in_addr) ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0), /* protocol == request ? */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - /* Sender Hardware Address must be different from our own */ - BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 4), /* A == X ? */ - BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */ - BPF_STMT(BPF_LDX + BPF_IMM, htobe32(a->s_addr)), /* X <- clients IP */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */ - BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */ - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == 0 ? */ - BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - }; - struct sock_fprog fprog = { - .len = ELEMENTSOF(filter), - .filter = (struct sock_filter*) filter, - }; - - assert(fd >= 0); - - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) - return -errno; - - return 0; -} - -int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac) { - union sockaddr_union link = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETH_P_ARP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; - _cleanup_close_ int s = -1; - int r; - - assert(ifindex > 0); - assert(mac); - - s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (s < 0) - return -errno; - - r = arp_update_filter(s, a, mac); - if (r < 0) - return r; - - if (bind(s, &link.sa, sizeof(link.ll)) < 0) - return -errno; - - return TAKE_FD(s); -} - -int arp_send_packet( - int fd, - int ifindex, - const struct in_addr *pa, - const struct ether_addr *ha, - bool announce) { - - union sockaddr_union link = { - .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htobe16(ETH_P_ARP), - .ll.sll_ifindex = ifindex, - .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; - struct ether_arp arp = { - .ea_hdr.ar_hrd = htobe16(ARPHRD_ETHER), /* HTYPE */ - .ea_hdr.ar_pro = htobe16(ETHERTYPE_IP), /* PTYPE */ - .ea_hdr.ar_hln = ETH_ALEN, /* HLEN */ - .ea_hdr.ar_pln = sizeof(struct in_addr), /* PLEN */ - .ea_hdr.ar_op = htobe16(ARPOP_REQUEST), /* REQUEST */ - }; - ssize_t n; - - assert(fd >= 0); - assert(ifindex > 0); - assert(pa); - assert(in4_addr_is_set(pa)); - assert(ha); - assert(!ether_addr_is_null(ha)); - - memcpy(&arp.arp_sha, ha, ETH_ALEN); - memcpy(&arp.arp_tpa, pa, sizeof(struct in_addr)); - - if (announce) - memcpy(&arp.arp_spa, pa, sizeof(struct in_addr)); - - n = sendto(fd, &arp, sizeof(struct ether_arp), 0, &link.sa, sizeof(link.ll)); - if (n < 0) - return -errno; - if (n != sizeof(struct ether_arp)) - return -EIO; - - return 0; -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/arp-util.h b/src/libnm-systemd-core/src/libsystemd-network/arp-util.h deleted file mode 100644 index b66a81bf9b..0000000000 --- a/src/libnm-systemd-core/src/libsystemd-network/arp-util.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -/*** - Copyright © 2014 Axis Communications AB. All rights reserved. -***/ - -#include <net/ethernet.h> -#include <netinet/in.h> - -#include "socket-util.h" -#include "sparse-endian.h" - -int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac); -int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac); - -int arp_send_packet( - int fd, - int ifindex, - const struct in_addr *pa, - const struct ether_addr *ha, - bool announce); -static inline int arp_send_probe( - int fd, - int ifindex, - const struct in_addr *pa, - const struct ether_addr *ha) { - return arp_send_packet(fd, ifindex, pa, ha, false); -} -static inline int arp_send_announcement( - int fd, - int ifindex, - const struct in_addr *pa, - const struct ether_addr *ha) { - return arp_send_packet(fd, ifindex, pa, ha, true); -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c index 68f6a7cb3c..a27d67a315 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c @@ -4,15 +4,11 @@ #include <net/ethernet.h> #include <net/if_arp.h> -#include "sd-device.h" -#include "sd-id128.h" - #include "dhcp-identifier.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" #include "string-table.h" -#include "udev-util.h" #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) @@ -207,48 +203,20 @@ int dhcp_identifier_set_duid( } int dhcp_identifier_set_iaid( - int ifindex, + sd_device *dev, const struct hw_addr_data *hw_addr, bool legacy_unstable_byteorder, - bool use_mac, void *ret) { - /* name is a pointer to memory in the sd_device struct, so must - * have the same scope */ - _cleanup_(sd_device_unrefp) sd_device *device = NULL; const char *name = NULL; uint32_t id32; uint64_t id; - int r; - assert(ifindex > 0); assert(hw_addr); assert(ret); - if (udev_available() && !use_mac) { - /* udev should be around */ - - r = sd_device_new_from_ifindex(&device, ifindex); - if (r < 0) - return r; - - r = sd_device_get_is_initialized(device); - if (r < 0) - return r; - if (r == 0) - /* not yet ready */ - return -EBUSY; - - r = device_is_renaming(device); - if (r < 0) - return r; - if (r > 0) - /* device is under renaming */ - return -EBUSY; - - name = net_get_persistent_name(device); - } - + if (dev) + name = net_get_persistent_name(dev); if (name) id = siphash24(name, strlen(name), HASH_KEY.bytes); else diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h index 8acb8c3210..523dfc4a71 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "sd-device.h" #include "sd-id128.h" #include "ether-addr-util.h" @@ -66,10 +67,9 @@ int dhcp_identifier_set_duid( struct duid *ret_duid, size_t *ret_len); int dhcp_identifier_set_iaid( - int ifindex, + sd_device *dev, const struct hw_addr_data *hw_addr, bool legacy_unstable_byteorder, - bool use_mac, void *ret); const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h deleted file mode 100644 index c67e9511a1..0000000000 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -/*** - Copyright © 2013 Intel Corporation. All rights reserved. -***/ - -#include "sd-dhcp-client.h" - -#include "dhcp-internal.h" -#include "dhcp-protocol.h" -#include "list.h" -#include "util.h" - -struct sd_dhcp_route { - struct in_addr dst_addr; - struct in_addr gw_addr; - unsigned char dst_prefixlen; -}; - -struct sd_dhcp_raw_option { - LIST_FIELDS(struct sd_dhcp_raw_option, options); - - uint8_t tag; - uint8_t length; - void *data; -}; - -struct sd_dhcp_lease { - unsigned n_ref; - - /* each 0 if unset */ - uint32_t t1; - uint32_t t2; - uint32_t lifetime; - - /* each 0 if unset */ - be32_t address; - be32_t server_address; - be32_t next_server; - - bool have_subnet_mask; - be32_t subnet_mask; - - bool have_broadcast; - be32_t broadcast; - - struct in_addr *router; - size_t router_size; - - DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; - - struct sd_dhcp_route *static_routes; - size_t n_static_routes; - struct sd_dhcp_route *classless_routes; - size_t n_classless_routes; - - uint16_t mtu; /* 0 if unset */ - - char *domainname; - char **search_domains; - char *hostname; - char *root_path; - - void *client_id; - size_t client_id_len; - - void *vendor_specific; - size_t vendor_specific_len; - - char *timezone; - - uint8_t sixrd_ipv4masklen; - uint8_t sixrd_prefixlen; - struct in6_addr sixrd_prefix; - struct in_addr *sixrd_br_addresses; - size_t sixrd_n_br_addresses; - - LIST_HEAD(struct sd_dhcp_raw_option, private_options); -}; - -int dhcp_lease_new(sd_dhcp_lease **ret); - -int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata); -int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains); -int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len); - -int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); - -int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h index 65f6cb057f..fa43f28eb5 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -48,6 +48,8 @@ struct sd_dhcp6_client { int event_priority; int fd; + sd_device *dev; + DHCP6State state; bool information_request; usec_t information_request_time_usec; @@ -77,8 +79,9 @@ struct sd_dhcp6_client { sd_dhcp6_client_callback_t callback; void *userdata; + bool send_release; - /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ + /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */ bool test_mode; }; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c index eedd92d3c2..a3e4e19e8e 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c @@ -23,7 +23,7 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), .in6.sin6_scope_id = ifindex, }; - _cleanup_close_ int s = -1; + _cleanup_close_ int s = -EBADF; int r; assert(ifindex > 0); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index 62873e0111..a6b74e07b2 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -495,13 +495,18 @@ int dhcp6_option_parse( } int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) { + DHCP6Status status; + assert(data || data_len == 0); if (data_len < sizeof(uint16_t)) return -EBADMSG; + status = unaligned_read_be16(data); + if (ret_status_message) { - char *msg; + _cleanup_free_ char *msg = NULL; + const char *s; /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415. * Let's escape unsafe characters for safety. */ @@ -509,10 +514,14 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s if (!msg) return -ENOMEM; - *ret_status_message = msg; + s = dhcp6_message_status_to_string(status); + if (s && !strextend_with_separator(&msg, ": ", s)) + return -ENOMEM; + + *ret_status_message = TAKE_PTR(msg); } - return unaligned_read_be16(data); + return status; } static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { @@ -538,9 +547,8 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return r; if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA address or PD prefix option with non-zero status: %s%s%s", - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); + "Received an IA address or PD prefix option with non-zero status%s%s", + isempty(msg) ? "." : ": ", strempty(msg)); if (r < 0) /* Let's log but ignore the invalid status option. */ log_dhcp6_client_errno(client, r, @@ -746,9 +754,8 @@ int dhcp6_option_parse_ia( return r; if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA option with non-zero status: %s%s%s", - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); + "Received an IA option with non-zero status%s%s", + isempty(msg) ? "." : ": ", strempty(msg)); if (r < 0) log_dhcp6_client_errno(client, r, "Received an IA option with an invalid status sub option, ignoring: %m"); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c index f965ea40fe..be0f651f1a 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c @@ -11,6 +11,7 @@ static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { [DHCP6_STATE_BOUND] = "bound", [DHCP6_STATE_RENEW] = "renew", [DHCP6_STATE_REBIND] = "rebind", + [DHCP6_STATE_STOPPING] = "stopping", }; DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h index 18217691b7..c70f93203d 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h @@ -58,6 +58,7 @@ typedef enum DHCP6State { DHCP6_STATE_BOUND, DHCP6_STATE_RENEW, DHCP6_STATE_REBIND, + DHCP6_STATE_STOPPING, _DHCP6_STATE_MAX, _DHCP6_STATE_INVALID = -EINVAL, } DHCP6State; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index 375f984940..57dd91f81f 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -11,6 +11,7 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" +#include "device-util.h" #include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" @@ -299,9 +300,8 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { if (client->iaid_set) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr, + r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr, /* legacy_unstable_byteorder = */ true, - /* use_mac = */ client->test_mode, &iaid); if (r < 0) return r; @@ -498,6 +498,14 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) { return 0; } +int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) { + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + client->send_release = enable; + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -586,7 +594,8 @@ static int client_append_common_options_in_managed_mode( DHCP6_STATE_SOLICITATION, DHCP6_STATE_REQUEST, DHCP6_STATE_RENEW, - DHCP6_STATE_REBIND)); + DHCP6_STATE_REBIND, + DHCP6_STATE_STOPPING)); assert(buf); assert(*buf); assert(offset); @@ -603,9 +612,11 @@ static int client_append_common_options_in_managed_mode( return r; } - r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); - if (r < 0) - return r; + if (client->state != DHCP6_STATE_STOPPING) { + r = dhcp6_option_append_fqdn(buf, offset, client->fqdn); + if (r < 0) + return r; + } r = dhcp6_option_append_user_class(buf, offset, client->user_class); if (r < 0) @@ -636,6 +647,8 @@ static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) return DHCP6_MESSAGE_RENEW; case DHCP6_STATE_REBIND: return DHCP6_MESSAGE_REBIND; + case DHCP6_STATE_STOPPING: + return DHCP6_MESSAGE_RELEASE; default: assert_not_reached(); } @@ -679,6 +692,9 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off req_opts = p; break; + case DHCP6_STATE_STOPPING: + return 0; + default: n = client->n_req_opts; req_opts = client->req_opts; @@ -690,6 +706,22 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **buf, size_t *off return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts); } +static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *offset) { + assert(client); + assert(buf); + assert(*buf); + assert(offset); + + if (!client->mudurl) + return 0; + + if (client->state == DHCP6_STATE_STOPPING) + return 0; + + return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); +} + int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ uint8_t *buf = NULL; struct in6_addr all_servers = @@ -735,7 +767,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - + case DHCP6_STATE_STOPPING: r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); @@ -753,18 +785,15 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { return r; break; - case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: + case DHCP6_STATE_STOPPED: default: assert_not_reached(); } - if (client->mudurl) { - r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_MUD_URL_V6, - strlen(client->mudurl), client->mudurl); - if (r < 0) - return r; - } + r = client_append_mudurl(client, &buf, &offset); + if (r < 0) + return r; r = client_append_oro(client, &buf, &offset); if (r < 0) @@ -856,6 +885,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda break; case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: case DHCP6_STATE_BOUND: default: assert_not_reached(); @@ -911,6 +941,7 @@ static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); break; case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: case DHCP6_STATE_BOUND: default: assert_not_reached(); @@ -1293,7 +1324,7 @@ static int client_receive_message( if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP && cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) - triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); + triple_timestamp_from_realtime(&t, timeval_load(CMSG_TYPED_DATA(cmsg, struct timeval))); } if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) @@ -1319,6 +1350,7 @@ static int client_receive_message( case DHCP6_STATE_BOUND: case DHCP6_STATE_STOPPED: + case DHCP6_STATE_STOPPING: default: assert_not_reached(); } @@ -1326,10 +1358,37 @@ static int client_receive_message( return 0; } +static int client_send_release(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + + assert(client); + + if (!client->send_release) + return 0; + + if (sd_dhcp6_client_get_lease(client, &lease) < 0) + return 0; + + if (!lease->ia_na && !lease->ia_pd) + return 0; + + client_set_state(client, DHCP6_STATE_STOPPING); + return dhcp6_client_send_message(client); +} + int sd_dhcp6_client_stop(sd_dhcp6_client *client) { + int r; + if (!client) return 0; + /* Intentionally ignoring failure to send DHCP6 release. The DHCPv6 client + * engine is about to release its UDP socket unconditionally. */ + r = client_send_release(client); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Failed to send DHCP6 release message, ignoring: %m"); + client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); client->receive_message = sd_event_source_unref(client->receive_message); @@ -1446,6 +1505,12 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { return client->event; } +int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev) { + assert_return(client, -EINVAL); + + return device_unref_and_replace(client->dev, dev); +} + static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { if (!client) return NULL; @@ -1461,6 +1526,8 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { client->fd = safe_close(client->fd); + sd_device_unref(client->dev); + free(client->req_opts); free(client->fqdn); free(client->mudurl); @@ -1491,7 +1558,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .ia_pd.type = SD_DHCP6_OPTION_IA_PD, .ifindex = -1, .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, - .fd = -1, + .fd = -EBADF, .rapid_commit = true, }; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index 57c23965ed..d14c412c1f 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -510,13 +510,11 @@ static int dhcp6_lease_parse_message( r = dhcp6_option_parse_status(optval, optlen, &msg); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m"); - if (r > 0) return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r), - "Received %s message with non-zero status: %s%s%s", + "Received %s message with non-zero status%s%s", dhcp6_message_type_to_string(message->type), - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); + isempty(msg) ? "." : ": ", strempty(msg)); break; } case SD_DHCP6_OPTION_IA_NA: { @@ -619,7 +617,7 @@ static int dhcp6_lease_parse_message( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received information refresh time option with an invalid length (%zu).", optlen); - irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; + irt = unaligned_read_be32(optval) * USEC_PER_SEC; break; } } diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h new file mode 100644 index 0000000000..a1b5e91edf --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stdbool.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sd-device.h" + +#include "log.h" +#include "macro.h" + +#define device_unref_and_replace(a, b) \ + unref_and_replace_full(a, b, sd_device_ref, sd_device_unref) + +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ + for (key = sd_device_get_property_first(device, &(value)); \ + key; \ + key = sd_device_get_property_next(device, &(value))) + +#define FOREACH_DEVICE_TAG(device, tag) \ + for (tag = sd_device_get_tag_first(device); \ + tag; \ + tag = sd_device_get_tag_next(device)) + +#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \ + for (tag = sd_device_get_current_tag_first(device); \ + tag; \ + tag = sd_device_get_current_tag_next(device)) + +#define FOREACH_DEVICE_SYSATTR(device, attr) \ + for (attr = sd_device_get_sysattr_first(device); \ + attr; \ + attr = sd_device_get_sysattr_next(device)) + +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ + for (devlink = sd_device_get_devlink_first(device); \ + devlink; \ + devlink = sd_device_get_devlink_next(device)) + +#define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \ + for (child = sd_device_get_child_first(device, suffix_ptr); \ + child; \ + child = sd_device_get_child_next(device, suffix_ptr)) + +#define FOREACH_DEVICE_CHILD(device, child) \ + _FOREACH_DEVICE_CHILD(device, child, NULL) + +#define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \ + _FOREACH_DEVICE_CHILD(device, child, &suffix) + +#define FOREACH_DEVICE(enumerator, device) \ + for (device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_device_next(enumerator)) + +#define FOREACH_SUBSYSTEM(enumerator, device) \ + for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ + device = sd_device_enumerator_get_subsystem_next(enumerator)) + +#define log_device_full_errno_zerook(device, level, error, ...) \ + ({ \ + const char *_sysname = NULL; \ + sd_device *_d = (device); \ + int _level = (level), _e = (error); \ + \ + if (_d && _unlikely_(log_get_max_level() >= LOG_PRI(_level))) \ + (void) sd_device_get_sysname(_d, &_sysname); \ + log_object_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, \ + _sysname ? "DEVICE=" : NULL, _sysname, \ + NULL, NULL, __VA_ARGS__); \ + }) + +#define log_device_full_errno(device, level, error, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_device_full_errno_zerook(device, level, _error, __VA_ARGS__); \ + }) + +#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__) + +#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__) +#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__) +#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__) +#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__) +#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__) + +#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__) +#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__) +#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__) +#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__) +#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__) + +int devname_from_devnum(mode_t mode, dev_t devnum, char **ret); +static inline int devname_from_stat_rdev(const struct stat *st, char **ret) { + assert(st); + return devname_from_devnum(st->st_mode, st->st_rdev, ret); +} +int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret); + +char** device_make_log_fields(sd_device *device); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c index a36eba9029..9863b07653 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c @@ -111,7 +111,7 @@ int event_reset_time_relative( int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) { _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; int r; assert(e); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index 778070a5fb..cefe2a36b4 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -16,6 +16,7 @@ #include "glyph-util.h" #include "hashmap.h" #include "list.h" +#include "logarithm.h" #include "macro.h" #include "memory-util.h" #include "missing_syscall.h" @@ -377,22 +378,22 @@ _public_ int sd_event_new(sd_event** ret) { *e = (sd_event) { .n_ref = 1, - .epoll_fd = -1, - .watchdog_fd = -1, + .epoll_fd = -EBADF, + .watchdog_fd = -EBADF, .realtime.wakeup = WAKEUP_CLOCK_DATA, - .realtime.fd = -1, + .realtime.fd = -EBADF, .realtime.next = USEC_INFINITY, .boottime.wakeup = WAKEUP_CLOCK_DATA, - .boottime.fd = -1, + .boottime.fd = -EBADF, .boottime.next = USEC_INFINITY, .monotonic.wakeup = WAKEUP_CLOCK_DATA, - .monotonic.fd = -1, + .monotonic.fd = -EBADF, .monotonic.next = USEC_INFINITY, .realtime_alarm.wakeup = WAKEUP_CLOCK_DATA, - .realtime_alarm.fd = -1, + .realtime_alarm.fd = -EBADF, .realtime_alarm.next = USEC_INFINITY, .boottime_alarm.wakeup = WAKEUP_CLOCK_DATA, - .boottime_alarm.fd = -1, + .boottime_alarm.fd = -EBADF, .boottime_alarm.next = USEC_INFINITY, .perturb = USEC_INFINITY, .original_pid = getpid_cached(), @@ -642,7 +643,7 @@ static int event_make_signal_data( *d = (struct signal_data) { .wakeup = WAKEUP_SIGNAL_DATA, - .fd = -1, + .fd = -EBADF, .priority = priority, }; @@ -658,7 +659,9 @@ static int event_make_signal_data( ss_copy = d->sigset; assert_se(sigaddset(&ss_copy, sig) >= 0); - r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); + r = signalfd(d->fd >= 0 ? d->fd : -1, /* the first arg must be -1 or a valid signalfd */ + &ss_copy, + SFD_NONBLOCK|SFD_CLOEXEC); if (r < 0) { r = -errno; goto fail; @@ -1177,7 +1180,7 @@ static int event_setup_timer_fd( if (_likely_(d->fd >= 0)) return 0; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; fd = timerfd_create(clock, TFD_NONBLOCK|TFD_CLOEXEC); if (fd < 0) @@ -1513,7 +1516,7 @@ _public_ int sd_event_add_child( } else s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */ } else - s->child.pidfd = -1; + s->child.pidfd = -EBADF; if (EVENT_SOURCE_WATCH_PIDFD(s)) { /* We have a pidfd and we only want to watch for exit */ @@ -1776,7 +1779,7 @@ static int event_make_inotify_data( int64_t priority, struct inotify_data **ret) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; struct inotify_data *d; int r; @@ -1973,7 +1976,7 @@ static int event_make_inode_data( .dev = dev, .ino = ino, .wd = -1, - .fd = -1, + .fd = -EBADF, .inotify_data = inotify_data, }; @@ -2069,7 +2072,7 @@ static int event_add_inotify_fd_internal( sd_event_inotify_handler_t callback, void *userdata) { - _cleanup_close_ int donated_fd = donate ? fd : -1; + _cleanup_close_ int donated_fd = donate ? fd : -EBADF; _cleanup_(source_freep) sd_event_source *s = NULL; struct inotify_data *inotify_data = NULL; struct inode_data *inode_data = NULL; @@ -2171,9 +2174,9 @@ _public_ int sd_event_add_inotify( assert_return(path, -EINVAL); - fd = open(path, O_PATH|O_CLOEXEC| - (mask & IN_ONLYDIR ? O_DIRECTORY : 0)| - (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0)); + fd = open(path, O_PATH | O_CLOEXEC | + (mask & IN_ONLYDIR ? O_DIRECTORY : 0) | + (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0)); if (fd < 0) return -errno; @@ -2723,6 +2726,9 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec assert_return(s, -EINVAL); assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM); + if (usec == USEC_INFINITY) + return sd_event_source_set_time(s, USEC_INFINITY); + r = sd_event_now(s->event, event_source_type_to_clock(s->type), &t); if (r < 0) return r; @@ -3176,7 +3182,7 @@ static int event_arm_timer( assert_se(d->fd >= 0); if (t == 0) { - /* We don' want to disarm here, just mean some time looooong ago. */ + /* We don't want to disarm here, just mean some time looooong ago. */ its.it_value.tv_sec = 0; its.it_value.tv_nsec = 1; } else @@ -3773,12 +3779,9 @@ static int event_prepare(sd_event *e) { break; s->prepare_iteration = e->iteration; - r = prioq_reshuffle(e->prepare, s, &s->prepare_index); - if (r < 0) - return r; + prioq_reshuffle(e->prepare, s, &s->prepare_index); assert(s->prepare); - s->dispatching = true; r = s->prepare(s, s->userdata); s->dispatching = false; @@ -3968,15 +3971,18 @@ static int epoll_wait_usec( usec_t timeout) { int msec; -#if 0 + /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. */ + +#if HAVE_EPOLL_PWAIT2 static bool epoll_pwait2_absent = false; int r; - /* 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. */ + /* epoll_pwait2() was added to Linux 5.11 (2021-02-14) and to glibc in 2.35 (2022-02-03). In contrast + * to other syscalls we don't bother with our own fallback syscall wrappers on old libcs, since this + * is not that obvious to implement given the libc and kernel definitions differ in the last + * argument. Moreover, the only reason to use it is the more accurate time-outs (which is not a + * biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's + * missing. */ if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { r = epoll_pwait2(fd, diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c index 4f52c14f64..a009a110a9 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c @@ -13,59 +13,49 @@ #include "sync-util.h" bool id128_is_valid(const char *s) { - size_t i, l; + size_t l; assert(s); l = strlen(s); - if (l == 32) { + if (l == SD_ID128_STRING_MAX - 1) /* Plain formatted 128bit hex string */ + return in_charset(s, HEXDIGITS); - for (i = 0; i < l; i++) { - char c = s[i]; - - if (!ascii_isdigit(c) && - !(c >= 'a' && c <= 'f') && - !(c >= 'A' && c <= 'F')) - return false; - } - - } else if (l == 36) { - + if (l == SD_ID128_UUID_STRING_MAX - 1) { /* Formatted UUID */ - - for (i = 0; i < l; i++) { + for (size_t i = 0; i < l; i++) { char c = s[i]; if (IN_SET(i, 8, 13, 18, 23)) { if (c != '-') return false; - } else { - if (!ascii_isdigit(c) && - !(c >= 'a' && c <= 'f') && - !(c >= 'A' && c <= 'F')) - return false; - } + } else if (!ascii_ishex(c)) + return false; } + return true; + } - } else - return false; - - return true; + return false; } -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { - char buffer[36 + 2]; +int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret) { + char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ ssize_t l; + int r; assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you - * accept". */ + * accept". + * + * This returns the following: + * -ENOMEDIUM: an empty string, + * -ENOPKG: "uninitialized" or "uninitialized\n", + * -EUCLEAN: other invalid strings. */ l = loop_read(fd, buffer, sizeof(buffer), false); /* we expect a short read of either 32/33 or 36/37 chars */ if (l < 0) @@ -75,44 +65,44 @@ int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret) { switch (l) { - case 13: - case 14: - /* Treat an "uninitialized" id file like an empty one */ - return f == ID128_PLAIN_OR_UNINIT && strneq(buffer, "uninitialized\n", l) ? -ENOMEDIUM : -EINVAL; + case STRLEN("uninitialized"): + case STRLEN("uninitialized\n"): + return strneq(buffer, "uninitialized\n", l) ? -ENOPKG : -EINVAL; - case 33: /* plain UUID with trailing newline */ - if (buffer[32] != '\n') - return -EINVAL; + case SD_ID128_STRING_MAX: /* plain UUID with trailing newline */ + if (buffer[SD_ID128_STRING_MAX-1] != '\n') + return -EUCLEAN; _fallthrough_; - case 32: /* plain UUID without trailing newline */ - if (f == ID128_UUID) - return -EINVAL; + case SD_ID128_STRING_MAX-1: /* plain UUID without trailing newline */ + if (!FLAGS_SET(f, ID128_FORMAT_PLAIN)) + return -EUCLEAN; - buffer[32] = 0; + buffer[SD_ID128_STRING_MAX-1] = 0; break; - case 37: /* RFC UUID with trailing newline */ - if (buffer[36] != '\n') - return -EINVAL; + case SD_ID128_UUID_STRING_MAX: /* RFC UUID with trailing newline */ + if (buffer[SD_ID128_UUID_STRING_MAX-1] != '\n') + return -EUCLEAN; _fallthrough_; - case 36: /* RFC UUID without trailing newline */ - if (IN_SET(f, ID128_PLAIN, ID128_PLAIN_OR_UNINIT)) - return -EINVAL; + case SD_ID128_UUID_STRING_MAX-1: /* RFC UUID without trailing newline */ + if (!FLAGS_SET(f, ID128_FORMAT_UUID)) + return -EUCLEAN; - buffer[36] = 0; + buffer[SD_ID128_UUID_STRING_MAX-1] = 0; break; default: - return -EINVAL; + return -EUCLEAN; } - return sd_id128_from_string(buffer, ret); + r = sd_id128_from_string(buffer, ret); + return r == -EINVAL ? -EUCLEAN : r; } -int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { - _cleanup_close_ int fd = -1; +int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret) { + _cleanup_close_ int fd = -EBADF; fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) @@ -121,29 +111,28 @@ int id128_read(const char *p, Id128Format f, sd_id128_t *ret) { return id128_read_fd(fd, f, ret); } -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { - char buffer[36 + 2]; +int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id) { + char buffer[SD_ID128_UUID_STRING_MAX + 1]; /* +1 is for trailing newline */ size_t sz; int r; assert(fd >= 0); - assert(f < _ID128_FORMAT_MAX); + assert(IN_SET((f & ID128_FORMAT_ANY), ID128_FORMAT_PLAIN, ID128_FORMAT_UUID)); - if (f != ID128_UUID) { + if (FLAGS_SET(f, ID128_FORMAT_PLAIN)) { assert_se(sd_id128_to_string(id, buffer)); - buffer[SD_ID128_STRING_MAX - 1] = '\n'; sz = SD_ID128_STRING_MAX; } else { assert_se(sd_id128_to_uuid_string(id, buffer)); - buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n'; sz = SD_ID128_UUID_STRING_MAX; } + buffer[sz - 1] = '\n'; r = loop_write(fd, buffer, sz, false); if (r < 0) return r; - if (do_sync) { + if (FLAGS_SET(f, ID128_SYNC_ON_WRITE)) { r = fsync_full(fd); if (r < 0) return r; @@ -152,14 +141,14 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { return 0; } -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync) { - _cleanup_close_ int fd = -1; +int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id) { + _cleanup_close_ int fd = -EBADF; fd = open(p, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, 0444); if (fd < 0) return -errno; - return id128_write_fd(fd, f, id, do_sync); + return id128_write_fd(fd, f, id); } void id128_hash_func(const sd_id128_t *p, struct siphash *state) { @@ -184,6 +173,7 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id) { } DEFINE_HASH_OPS(id128_hash_ops, sd_id128_t, id128_hash_func, id128_compare_func); +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(id128_hash_ops_free, sd_id128_t, id128_hash_func, id128_compare_func, free); int id128_get_product(sd_id128_t *ret) { sd_id128_t uuid; @@ -194,9 +184,9 @@ int id128_get_product(sd_id128_t *ret) { /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ - r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid); + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid); if (r == -ENOENT) - r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid); + r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid); if (r < 0) return r; diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h index 17b180c10c..e094de6441 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h @@ -10,27 +10,32 @@ bool id128_is_valid(const char *s) _pure_; -typedef enum Id128Format { - ID128_ANY, - ID128_PLAIN, /* formatted as 32 hex chars as-is */ - ID128_PLAIN_OR_UNINIT, /* formatted as 32 hex chars as-is; allow special "uninitialized" - * value when reading from file (id128_read() and id128_read_fd()). - * - * This format should be used when reading a machine-id file. */ - ID128_UUID, /* formatted as 36 character uuid string */ - _ID128_FORMAT_MAX, -} Id128Format; - -int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret); -int id128_read(const char *p, Id128Format f, sd_id128_t *ret); - -int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync); -int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync); +typedef enum Id128FormatFlag { + ID128_FORMAT_PLAIN = 1 << 0, /* formatted as 32 hex chars as-is */ + ID128_FORMAT_UUID = 1 << 1, /* formatted as 36 character uuid string */ + ID128_FORMAT_ANY = ID128_FORMAT_PLAIN | ID128_FORMAT_UUID, + + ID128_SYNC_ON_WRITE = 1 << 2, /* Sync the file after write. Used only when writing an ID. */ +} Id128FormatFlag; + +int id128_read_fd(int fd, Id128FormatFlag f, sd_id128_t *ret); +int id128_read(const char *p, Id128FormatFlag f, sd_id128_t *ret); + +int id128_write_fd(int fd, Id128FormatFlag f, sd_id128_t id); +int id128_write(const char *p, Id128FormatFlag f, sd_id128_t id); void id128_hash_func(const sd_id128_t *p, struct siphash *state); int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) _pure_; extern const struct hash_ops id128_hash_ops; +extern const struct hash_ops id128_hash_ops_free; sd_id128_t id128_make_v4_uuid(sd_id128_t id); int id128_get_product(sd_id128_t *ret); + +/* A helper to check for the three relevant cases of "machine ID not initialized" */ +#define ERRNO_IS_MACHINE_ID_UNSET(r) \ + IN_SET(abs(r), \ + ENOENT, \ + ENOMEDIUM, \ + ENOPKG) diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c index 709c8ffb57..ec3a496dba 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c @@ -15,18 +15,21 @@ #include "macro.h" #include "missing_syscall.h" #include "random-util.h" +#include "stat-util.h" #include "user-util.h" -#include "util.h" _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { + size_t k = 0; + assert_return(s, NULL); - for (size_t n = 0; n < 16; n++) { - s[n*2] = hexchar(id.bytes[n] >> 4); - s[n*2+1] = hexchar(id.bytes[n] & 0xF); + for (size_t n = 0; n < sizeof(sd_id128_t); n++) { + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); } - s[SD_ID128_STRING_MAX-1] = 0; + assert(k == SD_ID128_STRING_MAX - 1); + s[k] = 0; return s; } @@ -38,7 +41,7 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - for (size_t n = 0; n < 16; n++) { + for (size_t n = 0; n < sizeof(sd_id128_t); n++) { if (IN_SET(n, 4, 6, 8, 10)) s[k++] = '-'; @@ -53,14 +56,14 @@ _public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD return s; } -_public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { - unsigned n, i; +_public_ int sd_id128_from_string(const char *s, sd_id128_t *ret) { + size_t n, i; sd_id128_t t; bool is_guid = false; assert_return(s, -EINVAL); - for (n = 0, i = 0; n < 16;) { + for (n = 0, i = 0; n < sizeof(sd_id128_t);) { int a, b; if (s[i] == '-') { @@ -90,7 +93,7 @@ _public_ int sd_id128_from_string(const char s[], sd_id128_t *ret) { t.bytes[n++] = (a << 4) | b; } - if (i != (is_guid ? 36 : 32)) + if (i != (is_guid ? SD_ID128_UUID_STRING_MAX : SD_ID128_STRING_MAX) - 1) return -EINVAL; if (s[i] != 0) @@ -121,10 +124,8 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { static thread_local sd_id128_t saved_machine_id = {}; int r; - assert_return(ret, -EINVAL); - if (sd_id128_is_null(saved_machine_id)) { - r = id128_read("/etc/machine-id", ID128_PLAIN, &saved_machine_id); + r = id128_read("/etc/machine-id", ID128_FORMAT_PLAIN, &saved_machine_id); if (r < 0) return r; @@ -132,7 +133,8 @@ _public_ int sd_id128_get_machine(sd_id128_t *ret) { return -ENOMEDIUM; } - *ret = saved_machine_id; + if (ret) + *ret = saved_machine_id; return 0; } @@ -140,15 +142,19 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) { static thread_local sd_id128_t saved_boot_id = {}; int r; - assert_return(ret, -EINVAL); - if (sd_id128_is_null(saved_boot_id)) { - r = id128_read("/proc/sys/kernel/random/boot_id", ID128_UUID, &saved_boot_id); + r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID, &saved_boot_id); + if (r == -ENOENT && proc_mounted() == 0) + return -ENOSYS; if (r < 0) return r; + + if (sd_id128_is_null(saved_boot_id)) + return -ENOMEDIUM; } - *ret = saved_boot_id; + if (ret) + *ret = saved_boot_id; return 0; } @@ -198,22 +204,22 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { /* Chop off the final description string */ d = strrchr(description, ';'); if (!d) - return -EIO; + return -EUCLEAN; *d = 0; /* Look for the permissions */ p = strrchr(description, ';'); if (!p) - return -EIO; + return -EUCLEAN; errno = 0; perms = strtoul(p + 1, &e, 16); if (errno > 0) return -errno; if (e == p + 1) /* Read at least one character */ - return -EIO; + return -EUCLEAN; if (e != d) /* Must reached the end */ - return -EIO; + return -EUCLEAN; if ((perms & ~MAX_PERMS) != 0) return -EPERM; @@ -223,7 +229,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { /* Look for the group ID */ g = strrchr(description, ';'); if (!g) - return -EIO; + return -EUCLEAN; r = parse_gid(g + 1, &gid); if (r < 0) return r; @@ -234,7 +240,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { /* Look for the user ID */ u = strrchr(description, ';'); if (!u) - return -EIO; + return -EUCLEAN; r = parse_uid(u + 1, &uid); if (r < 0) return r; @@ -245,13 +251,14 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { if (c < 0) return -errno; if (c != sizeof(sd_id128_t)) - return -EIO; + return -EUCLEAN; return 0; } static int get_invocation_from_environment(sd_id128_t *ret) { const char *e; + int r; assert(ret); @@ -259,33 +266,31 @@ static int get_invocation_from_environment(sd_id128_t *ret) { if (!e) return -ENXIO; - return sd_id128_from_string(e, ret); + r = sd_id128_from_string(e, ret); + return r == -EINVAL ? -EUCLEAN : r; } _public_ int sd_id128_get_invocation(sd_id128_t *ret) { static thread_local sd_id128_t saved_invocation_id = {}; int r; - assert_return(ret, -EINVAL); - if (sd_id128_is_null(saved_invocation_id)) { /* We first check the environment. The environment variable is primarily relevant for user * services, and sufficiently safe as long as no privilege boundary is involved. */ r = get_invocation_from_environment(&saved_invocation_id); - if (r >= 0) { - *ret = saved_invocation_id; - return 0; - } else if (r != -ENXIO) - return r; - - /* The kernel keyring is relevant for system services (as for user services we don't store - * the invocation ID in the keyring, as there'd be no trust benefit in that). */ - r = get_invocation_from_keyring(&saved_invocation_id); + if (r == -ENXIO) + /* The kernel keyring is relevant for system services (as for user services we don't + * store the invocation ID in the keyring, as there'd be no trust benefit in that). */ + r = get_invocation_from_keyring(&saved_invocation_id); if (r < 0) return r; + + if (sd_id128_is_null(saved_invocation_id)) + return -ENOMEDIUM; } - *ret = saved_invocation_id; + if (ret) + *ret = saved_invocation_id; return 0; } diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h new file mode 100644 index 0000000000..e3d647f75d --- /dev/null +++ b/src/libnm-systemd-core/src/systemd/sd-device.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddevicehfoo +#define foosddevicehfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <https://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + +#include "sd-event.h" +#include "sd-id128.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_device sd_device; +typedef struct sd_device_enumerator sd_device_enumerator; +typedef struct sd_device_monitor sd_device_monitor; + +__extension__ typedef enum sd_device_action_t { + SD_DEVICE_ADD, + SD_DEVICE_REMOVE, + SD_DEVICE_CHANGE, + SD_DEVICE_MOVE, + SD_DEVICE_ONLINE, + SD_DEVICE_OFFLINE, + SD_DEVICE_BIND, + SD_DEVICE_UNBIND, + _SD_DEVICE_ACTION_MAX, + _SD_DEVICE_ACTION_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(DEVICE_ACTION) +} sd_device_action_t; + +/* callback */ + +typedef int (*sd_device_monitor_handler_t)(sd_device_monitor *m, sd_device *device, void *userdata); + +/* device */ + +sd_device *sd_device_ref(sd_device *device); +sd_device *sd_device_unref(sd_device *device); + +int sd_device_new_from_syspath(sd_device **ret, const char *syspath); +int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); +int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); +int sd_device_new_from_device_id(sd_device **ret, const char *id); +int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st); +int sd_device_new_from_devname(sd_device **ret, const char *devname); +int sd_device_new_from_path(sd_device **ret, const char *path); +int sd_device_new_from_ifname(sd_device **ret, const char *ifname); +int sd_device_new_from_ifindex(sd_device **ret, int ifindex); + +int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix); + +int sd_device_get_parent(sd_device *child, sd_device **ret); +int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret); + +int sd_device_get_syspath(sd_device *device, const char **ret); +int sd_device_get_subsystem(sd_device *device, const char **ret); +int sd_device_get_devtype(sd_device *device, const char **ret); +int sd_device_get_devnum(sd_device *device, dev_t *devnum); +int sd_device_get_ifindex(sd_device *device, int *ifindex); +int sd_device_get_driver(sd_device *device, const char **ret); +int sd_device_get_devpath(sd_device *device, const char **ret); +int sd_device_get_devname(sd_device *device, const char **ret); +int sd_device_get_sysname(sd_device *device, const char **ret); +int sd_device_get_sysnum(sd_device *device, const char **ret); +int sd_device_get_action(sd_device *device, sd_device_action_t *ret); +int sd_device_get_seqnum(sd_device *device, uint64_t *ret); +int sd_device_get_diskseq(sd_device *device, uint64_t *ret); + +int sd_device_get_is_initialized(sd_device *device); +int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret); +int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret); + +const char *sd_device_get_tag_first(sd_device *device); +const char *sd_device_get_tag_next(sd_device *device); +const char *sd_device_get_current_tag_first(sd_device *device); +const char *sd_device_get_current_tag_next(sd_device *device); +const char *sd_device_get_devlink_first(sd_device *device); +const char *sd_device_get_devlink_next(sd_device *device); +const char *sd_device_get_property_first(sd_device *device, const char **value); +const char *sd_device_get_property_next(sd_device *device, const char **value); +const char *sd_device_get_sysattr_first(sd_device *device); +const char *sd_device_get_sysattr_next(sd_device *device); +sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix); +sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix); + +int sd_device_has_tag(sd_device *device, const char *tag); +int sd_device_has_current_tag(sd_device *device, const char *tag); +int sd_device_get_property_value(sd_device *device, const char *key, const char **value); +int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret); +int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); + +int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value); +int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4); +int sd_device_trigger(sd_device *device, sd_device_action_t action); +int sd_device_trigger_with_uuid(sd_device *device, sd_device_action_t action, sd_id128_t *ret_uuid); +int sd_device_open(sd_device *device, int flags); + +/* device enumerator */ + +int sd_device_enumerator_new(sd_device_enumerator **ret); +sd_device_enumerator *sd_device_enumerator_ref(sd_device_enumerator *enumerator); +sd_device_enumerator *sd_device_enumerator_unref(sd_device_enumerator *enumerator); + +sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator); +sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumerator); + +int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); +int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); +int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname); +int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); +int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent); +int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator); + +/* device monitor */ + +int sd_device_monitor_new(sd_device_monitor **ret); +sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m); +sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m); + +int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size); +int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event); +int sd_device_monitor_detach_event(sd_device_monitor *m); +sd_event *sd_device_monitor_get_event(sd_device_monitor *m); +sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m); +int sd_device_monitor_set_description(sd_device_monitor *m, const char *description); +int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret); +int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata); +int sd_device_monitor_stop(sd_device_monitor *m); + +int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype); +int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag); +int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, const char *sysattr, const char *value, int match); +int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match); +int sd_device_monitor_filter_update(sd_device_monitor *m); +int sd_device_monitor_filter_remove(sd_device_monitor *m); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device, sd_device_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_enumerator, sd_device_enumerator_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_device_monitor, sd_device_monitor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h index 2c66c51b78..a9fa78569d 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h @@ -23,6 +23,7 @@ #include <net/ethernet.h> #include <sys/types.h> +#include "sd-device.h" #include "sd-dhcp6-lease.h" #include "sd-dhcp6-option.h" #include "sd-event.h" @@ -263,6 +264,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable); +int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, @@ -279,6 +281,7 @@ int sd_dhcp6_client_attach_event( int64_t priority); int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client); +int sd_dhcp6_client_attach_device(sd_dhcp6_client *client, sd_device *dev); sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client); sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client); int sd_dhcp6_client_new(sd_dhcp6_client **ret); diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c index b030f454b2..6063943c88 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.c +++ b/src/libnm-systemd-shared/src/basic/alloc-util.c @@ -102,3 +102,7 @@ void* greedy_realloc0( return q; } + +void *expand_to_usable(void *ptr, size_t newsize _unused_) { + return ptr; +} diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index b38db7d473..bf783b15a2 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -2,6 +2,7 @@ #pragma once #include <alloca.h> +#include <malloc.h> #include <stddef.h> #include <stdlib.h> #include <string.h> @@ -184,17 +185,35 @@ void* greedy_realloc0(void **p, size_t need, size_t size); # define msan_unpoison(r, s) #endif -/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), in a way that - * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the - * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of - * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by - * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the - * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory, - * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and - * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner - * case. */ +/* Dummy allocator to tell the compiler that the new size of p is newsize. The implementation returns the + * pointer as is; the only reason for its existence is as a conduit for the _alloc_ attribute. This must not + * be inlined (hence a non-static function with _noinline_ because LTO otherwise tries to inline it) because + * gcc then loses the attributes on the function. + * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96503 */ +void *expand_to_usable(void *p, size_t newsize) _alloc_(2) _returns_nonnull_ _noinline_; + +static inline size_t malloc_sizeof_safe(void **xp) { + if (_unlikely_(!xp || !*xp)) + return 0; + + size_t sz = malloc_usable_size(*xp); + *xp = expand_to_usable(*xp, sz); + /* GCC doesn't see the _returns_nonnull_ when built with ubsan, so yet another hint to make it doubly + * clear that expand_to_usable won't return NULL. + * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79265 */ + if (!*xp) + assert_not_reached(); + return sz; +} + +/* This returns the number of usable bytes in a malloc()ed region as per malloc_usable_size(), which may + * return a value larger than the size that was actually allocated. Access to that additional memory is + * discouraged because it violates the C standard; a compiler cannot see that this as valid. To help the + * compiler out, the MALLOC_SIZEOF_SAFE macro 'allocates' the usable size using a dummy allocator function + * expand_to_usable. There is a possibility of malloc_usable_size() returning different values during the + * lifetime of an object, which may cause problems, but the glibc allocator does not do that at the moment. */ #define MALLOC_SIZEOF_SAFE(x) \ - MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) + malloc_sizeof_safe((void**) &__builtin_choose_expr(__builtin_constant_p(x), (void*) { NULL }, (x))) /* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items * that fit into the specified memory block */ diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index df6d5b7bbb..c9aae5abf6 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -9,7 +9,7 @@ #include <sys/statfs.h> #include <sys/types.h> -#include "def.h" +#include "constants.h" #include "set.h" #define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd" diff --git a/src/libnm-systemd-shared/src/basic/constants.h b/src/libnm-systemd-shared/src/basic/constants.h new file mode 100644 index 0000000000..5d68cc6332 --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/constants.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#if !defined(HAS_FEATURE_MEMORY_SANITIZER) +# if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# define HAS_FEATURE_MEMORY_SANITIZER 1 +# endif +# endif +# if !defined(HAS_FEATURE_MEMORY_SANITIZER) +# define HAS_FEATURE_MEMORY_SANITIZER 0 +# endif +#endif + +#if !defined(HAS_FEATURE_ADDRESS_SANITIZER) +# ifdef __SANITIZE_ADDRESS__ +# define HAS_FEATURE_ADDRESS_SANITIZER 1 +# elif defined(__has_feature) +# if __has_feature(address_sanitizer) +# define HAS_FEATURE_ADDRESS_SANITIZER 1 +# endif +# endif +# if !defined(HAS_FEATURE_ADDRESS_SANITIZER) +# define HAS_FEATURE_ADDRESS_SANITIZER 0 +# endif +#endif + +#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) + +/* Many different things, but also system unit start/stop */ +#define DEFAULT_TIMEOUT_USEC (DEFAULT_TIMEOUT_SEC*USEC_PER_SEC) +/* User unit start/stop */ +#define DEFAULT_USER_TIMEOUT_USEC (DEFAULT_USER_TIMEOUT_SEC*USEC_PER_SEC) +/* Timeout for user confirmation on the console */ +#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) + +/* We use an extra-long timeout for the reload. This is because a reload or reexec means generators are rerun + * which are timed out after DEFAULT_TIMEOUT_USEC. Let's use twice that time here, so that the generators can + * have their timeout, and for everything else there's the same time budget in place. */ +#define DAEMON_RELOAD_TIMEOUT_SEC (DEFAULT_TIMEOUT_USEC * 2) + +#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) +#define DEFAULT_START_LIMIT_BURST 5 + +/* The default time after which exit-on-idle services exit. This + * should be kept lower than the watchdog timeout, because otherwise + * the watchdog pings will keep the loop busy. */ +#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC) + +/* The default value for the net.unix.max_dgram_qlen sysctl */ +#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512 + +#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT +#define SIGNALS_IGNORE SIGPIPE + +#define NOTIFY_FD_MAX 768 +#define NOTIFY_BUFFER_MAX PIPE_BUF + +#if HAVE_SPLIT_USR +# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0" +# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n +#else +# define _CONF_PATHS_SPLIT_USR_NULSTR(n) +# define _CONF_PATHS_SPLIT_USR(n) +#endif + +/* Return a nulstr for a standard cascade of configuration paths, + * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr() + * to implement drop-in directories for extending configuration + * files. */ +#define CONF_PATHS_NULSTR(n) \ + "/etc/" n "\0" \ + "/run/" n "\0" \ + "/usr/local/lib/" n "\0" \ + "/usr/lib/" n "\0" \ + _CONF_PATHS_SPLIT_USR_NULSTR(n) + +#define CONF_PATHS_USR(n) \ + "/etc/" n, \ + "/run/" n, \ + "/usr/local/lib/" n, \ + "/usr/lib/" n + +#define CONF_PATHS(n) \ + CONF_PATHS_USR(n) \ + _CONF_PATHS_SPLIT_USR(n) + +#define CONF_PATHS_USR_STRV(n) \ + STRV_MAKE(CONF_PATHS_USR(n)) + +#define CONF_PATHS_STRV(n) \ + STRV_MAKE(CONF_PATHS(n)) + +/* The limit for PID 1 itself (which is not inherited to children) */ +#define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL) + +/* Since kernel 5.16 the kernel default limit was raised to 8M. Let's adjust things on old kernels too, and + * in containers so that our children inherit that. */ +#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL) + +#define PLYMOUTH_SOCKET { \ + .un.sun_family = AF_UNIX, \ + .un.sun_path = "\0/org/freedesktop/plymouthd", \ + } + +/* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */ +#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM" +/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */ +#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM" + +#define KERNEL_BASELINE_VERSION "4.15" diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index e363bc80bf..45b0d901c5 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -12,11 +12,17 @@ #include "tmpfile-util.h" #include "utf8.h" +typedef int (*push_env_func_t)( + const char *filename, + unsigned line, + const char *key, + char *value, + void *userdata); + static int parse_env_file_internal( FILE *f, const char *fname, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata), + push_env_func_t push, void *userdata) { size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; @@ -37,6 +43,9 @@ static int parse_env_file_internal( COMMENT_ESCAPE } state = PRE_KEY; + assert(f || fname); + assert(push); + if (f) r = read_full_stream(f, &contents, NULL); else @@ -274,6 +283,8 @@ static int check_utf8ness_and_warn( const char *filename, unsigned line, const char *key, char *value) { + assert(key); + if (!utf8_is_valid(key)) { _cleanup_free_ char *p = NULL; @@ -304,6 +315,8 @@ static int parse_env_file_push( va_list aq, *ap = userdata; int r; + assert(key); + r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; @@ -338,6 +351,8 @@ int parse_env_filev( int r; va_list aq; + assert(f || fname); + va_copy(aq, ap); r = parse_env_file_internal(f, fname, parse_env_file_push, &aq); va_end(aq); @@ -352,6 +367,37 @@ int parse_env_file_sentinel( va_list ap; int r; + assert(f || fname); + + va_start(ap, fname); + r = parse_env_filev(f, fname, ap); + va_end(ap); + + return r; +} + +int parse_env_file_fd_sentinel( + int fd, + const char *fname, /* only used for logging */ + ...) { + + _cleanup_close_ int fd_ro = -EBADF; + _cleanup_fclose_ FILE *f = NULL; + va_list ap; + int r; + + assert(fd >= 0); + + fd_ro = fd_reopen(fd, O_CLOEXEC | O_RDONLY); + if (fd_ro < 0) + return fd_ro; + + f = fdopen(fd_ro, "re"); + if (!f) + return -errno; + + TAKE_FD(fd_ro); + va_start(ap, fname); r = parse_env_filev(f, fname, ap); va_end(ap); @@ -363,10 +409,13 @@ static int load_env_file_push( const char *filename, unsigned line, const char *key, char *value, void *userdata) { + char ***m = userdata; char *p; int r; + assert(key); + r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; @@ -383,15 +432,18 @@ static int load_env_file_push( return 0; } -int load_env_file(FILE *f, const char *fname, char ***rl) { +int load_env_file(FILE *f, const char *fname, char ***ret) { _cleanup_strv_free_ char **m = NULL; int r; + assert(f || fname); + assert(ret); + r = parse_env_file_internal(f, fname, load_env_file_push, &m); if (r < 0) return r; - *rl = TAKE_PTR(m); + *ret = TAKE_PTR(m); return 0; } @@ -403,6 +455,8 @@ static int load_env_file_push_pairs( char ***m = ASSERT_PTR(userdata); int r; + assert(key); + r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; @@ -426,15 +480,17 @@ static int load_env_file_push_pairs( return strv_extend(m, ""); } -int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { +int load_env_file_pairs(FILE *f, const char *fname, char ***ret) { _cleanup_strv_free_ char **m = NULL; int r; + assert(f || fname); + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m); if (r < 0) return r; - *rl = TAKE_PTR(m); + *ret = TAKE_PTR(m); return 0; } @@ -446,6 +502,8 @@ static int merge_env_file_push( char ***env = ASSERT_PTR(userdata); char *expanded_value; + assert(key); + if (!value) { log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); return 0; @@ -476,6 +534,9 @@ int merge_env_file( FILE *f, const char *fname) { + assert(env); + assert(f || fname); + /* NOTE: this function supports braceful and braceless variable expansions, * plus "extended" substitutions, unlike other exported parsing functions. */ @@ -486,6 +547,9 @@ int merge_env_file( static void write_env_var(FILE *f, const char *v) { const char *p; + assert(f); + assert(v); + p = strchr(v, '='); if (!p) { /* Fallback */ diff --git a/src/libnm-systemd-shared/src/basic/env-file.h b/src/libnm-systemd-shared/src/basic/env-file.h index de475885ac..2448d943cd 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.h +++ b/src/libnm-systemd-shared/src/basic/env-file.h @@ -9,8 +9,10 @@ int parse_env_filev(FILE *f, const char *fname, va_list ap); int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; #define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) -int load_env_file(FILE *f, const char *fname, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, char ***l); +int parse_env_file_fd_sentinel(int fd, const char *fname, ...) _sentinel_; +#define parse_env_file_fd(fd, fname, ...) parse_env_file_fd_sentinel(fd, fname, __VA_ARGS__, NULL) +int load_env_file(FILE *f, const char *fname, char ***ret); +int load_env_file_pairs(FILE *f, const char *fname, char ***ret); int merge_env_file(char ***env, FILE *f, const char *fname); diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c index 1cb7ced545..e04b435d5b 100644 --- a/src/libnm-systemd-shared/src/basic/escape.c +++ b/src/libnm-systemd-shared/src/basic/escape.c @@ -445,31 +445,30 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl } char* octescape(const char *s, size_t len) { - char *r, *t; - const char *f; + char *buf, *t; - /* Escapes all chars in bad, in addition to \ and " chars, - * in \nnn style escaping. */ + /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */ - r = new(char, len * 4 + 1); - if (!r) + assert(s || len == 0); + + t = buf = new(char, len * 4 + 1); + if (!buf) return NULL; - for (f = s, t = r; f < s + len; f++) { + for (size_t i = 0; i < len; i++) { + uint8_t u = (uint8_t) s[i]; - if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) { + if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"')) { *(t++) = '\\'; - *(t++) = '0' + (*f >> 6); - *(t++) = '0' + ((*f >> 3) & 8); - *(t++) = '0' + (*f & 8); + *(t++) = '0' + (u >> 6); + *(t++) = '0' + ((u >> 3) & 7); + *(t++) = '0' + (u & 7); } else - *(t++) = *f; + *(t++) = u; } *t = 0; - - return r; - + return buf; } static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) { diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index cee20a9a81..4d6d01cd99 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -29,7 +29,6 @@ #include "stat-util.h" #include "stdio-util.h" #include "tmpfile-util.h" -#include "util.h" /* The maximum number of iterations in the loop to close descriptors in the fallback case * when /proc/self/fd/ is inaccessible. */ @@ -57,11 +56,9 @@ int close_nointr(int fd) { } int safe_close(int fd) { - /* - * Like close_nointr() but cannot fail. Guarantees errno is - * unchanged. Is a NOP with negative fds passed, and returns - * -1, so that it can be used in this syntax: + * Like close_nointr() but cannot fail. Guarantees errno is unchanged. Is a noop for negative fds, + * and returns -EBADF, so that it can be used in this syntax: * * fd = safe_close(fd); */ @@ -77,7 +74,7 @@ int safe_close(int fd) { assert_se(close_nointr(fd) != -EBADF); } - return -1; + return -EBADF; } void safe_close_pair(int p[static 2]) { @@ -174,12 +171,35 @@ int fd_cloexec(int fd, bool cloexec) { return RET_NERRNO(fcntl(fd, F_SETFD, nflags)); } +int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) { + int ret = 0, r; + + assert(n_fds == 0 || fds); + + for (size_t i = 0; i < n_fds; i++) { + if (fds[i] < 0) /* Skip gracefully over already invalidated fds */ + continue; + + r = fd_cloexec(fds[i], cloexec); + if (r < 0 && ret >= 0) /* Continue going, but return first error */ + ret = r; + else + ret = 1; /* report if we did anything */ + } + + return ret; +} + _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) { assert(n_fdset == 0 || fdset); - for (size_t i = 0; i < n_fdset; i++) + for (size_t i = 0; i < n_fdset; i++) { + if (fdset[i] < 0) + continue; + if (fdset[i] == fd) return true; + } return false; } @@ -226,7 +246,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) { "Refusing to loop over %d potential fds.", max_fd); - for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { + for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) { int q; if (fd_in_set(fd, except, n_except)) @@ -252,6 +272,10 @@ static int close_all_fds_special_case(const int except[], size_t n_except) { if (!have_close_range) return 0; + if (n_except == 1 && except[0] < 0) /* Minor optimization: if we only got one fd, and it's invalid, + * we got none */ + n_except = 0; + switch (n_except) { case 0: @@ -386,7 +410,7 @@ int close_all_fds(const int except[], size_t n_except) { return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */ FOREACH_DIRENT(de, d, return -errno) { - int fd = -1, q; + int fd = -EBADF, q; if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) continue; @@ -475,7 +499,8 @@ void cmsg_close_all(struct msghdr *mh) { CMSG_FOREACH(cmsg, mh) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + close_many(CMSG_TYPED_DATA(cmsg, int), + (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); } bool fdname_is_valid(const char *s) { @@ -604,25 +629,23 @@ int fd_move_above_stdio(int fd) { } int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd) { - - int fd[3] = { /* Put together an array of fds we work on */ - original_input_fd, - original_output_fd, - original_error_fd - }; - - int r, i, - null_fd = -1, /* if we open /dev/null, we store the fd to it here */ - copy_fd[3] = { -1, -1, -1 }; /* This contains all fds we duplicate here temporarily, and hence need to close at the end */ + int fd[3] = { original_input_fd, /* Put together an array of fds we work on */ + original_output_fd, + original_error_fd }, + null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */ + copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here + * temporarily, and hence need to close at the end. */ + r; bool null_readable, null_writable; - /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors is - * specified as -1 it will be connected with /dev/null instead. If any of the file descriptors is passed as - * itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is turned off should it be - * on. + /* Sets up stdin, stdout, stderr with the three file descriptors passed in. If any of the descriptors + * is specified as -EBADF it will be connected with /dev/null instead. If any of the file descriptors + * is passed as itself (e.g. stdin as STDIN_FILENO) it is left unmodified, but the O_CLOEXEC bit is + * turned off should it be on. * - * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and on - * failure! Thus, callers should assume that when this function returns the input fds are invalidated. + * Note that if any of the passed file descriptors are > 2 they will be closed — both on success and + * on failure! Thus, callers should assume that when this function returns the input fds are + * invalidated. * * Note that when this function fails stdin/stdout/stderr might remain half set up! * @@ -658,7 +681,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ } /* Let's assemble fd[] with the fds to install in place of stdin/stdout/stderr */ - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { if (fd[i] < 0) fd[i] = null_fd; /* A negative parameter means: connect this one to /dev/null */ @@ -674,10 +697,10 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_ } } - /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that we - * have freedom to move them around. If the fds already were at the right places then the specific fds are - * -1. Let's now move them to the right places. This is the point of no return. */ - for (i = 0; i < 3; i++) { + /* At this point we now have the fds to use in fd[], and they are all above the stdio range, so that + * we have freedom to move them around. If the fds already were at the right places then the specific + * fds are -EBADF. Let's now move them to the right places. This is the point of no return. */ + for (int i = 0; i < 3; i++) { if (fd[i] == i) { @@ -708,7 +731,7 @@ finish: safe_close_above_stdio(original_error_fd); /* Close the copies we moved > 2 */ - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) safe_close(copy_fd[i]); /* Close our null fd, if it's > 2 */ @@ -754,6 +777,37 @@ int fd_reopen(int fd, int flags) { return new_fd; } +int fd_reopen_condition( + int fd, + int flags, + int mask, + int *ret_new_fd) { + + int r, new_fd; + + assert(fd >= 0); + + /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified + * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if + * needed, but only then. */ + + r = fcntl(fd, F_GETFL); + if (r < 0) + return -errno; + + if ((r & mask) == (flags & mask)) { + *ret_new_fd = -EBADF; + return fd; + } + + new_fd = fd_reopen(fd, flags); + if (new_fd < 0) + return new_fd; + + *ret_new_fd = new_fd; + return new_fd; +} + int read_nr_open(void) { _cleanup_free_ char *nr_open = NULL; int r; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index d9896e27e8..952afdd64f 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -15,14 +15,15 @@ /* Make sure we can distinguish fd 0 and NULL */ #define FD_TO_PTR(fd) INT_TO_PTR((fd)+1) #define PTR_TO_FD(p) (PTR_TO_INT(p)-1) +#define PIPE_EBADF { -EBADF, -EBADF } int close_nointr(int fd); int safe_close(int fd); void safe_close_pair(int p[static 2]); static inline int safe_close_above_stdio(int fd) { - if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -1 */ - return -1; + if (fd < 3) /* Don't close stdin/stdout/stderr, but still invalidate the fd by returning -EBADF. */ + return -EBADF; return safe_close(fd); } @@ -56,6 +57,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL); int fd_nonblock(int fd, bool nonblock); int fd_cloexec(int fd, bool cloexec); +int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec); int get_max_fd(void); @@ -85,17 +87,11 @@ int fd_move_above_stdio(int fd); int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); static inline int make_null_stdio(void) { - return rearrange_stdio(-1, -1, -1); + return rearrange_stdio(-EBADF, -EBADF, -EBADF); } -/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */ -#define TAKE_FD(fd) \ - ({ \ - int *_fd_ = &(fd); \ - int _ret_ = *_fd_; \ - *_fd_ = -1; \ - _ret_; \ - }) +/* Like TAKE_PTR() but for file descriptors, resetting them to -EBADF */ +#define TAKE_FD(fd) TAKE_GENERIC(fd, int, -EBADF) /* Like free_and_replace(), but for file descriptors */ #define close_and_replace(a, b) \ @@ -107,6 +103,7 @@ static inline int make_null_stdio(void) { }) int fd_reopen(int fd, int flags); +int fd_reopen_condition(int fd, int flags, int mask, int *ret_new_fd); int read_nr_open(void); int fd_get_diskseq(int fd, uint64_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index 2c4ba89a15..51b11ab9c7 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -21,6 +21,7 @@ #include "log.h" #include "macro.h" #include "mkdir.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "socket-util.h" @@ -43,16 +44,17 @@ * can detect EOFs. */ #define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U) -int fopen_unlocked(const char *path, const char *options, FILE **ret) { +int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret) { + int r; + assert(ret); - FILE *f = fopen(path, options); - if (!f) - return -errno; + r = xfopenat(dir_fd, path, options, flags, ret); + if (r < 0) + return r; - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) __fsetlocking(*ret, FSETLOCKING_BYCALLER); - *ret = f; return 0; } @@ -78,7 +80,7 @@ int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) { if (r < 0) return r; - *fd = -1; + *fd = -EBADF; return 0; } @@ -90,7 +92,7 @@ FILE* take_fdopen(int *fd, const char *options) { if (!f) return NULL; - *fd = -1; + *fd = -EBADF; return f; } @@ -102,7 +104,7 @@ DIR* take_fdopendir(int *dfd) { if (!d) return NULL; - *dfd = -1; + *dfd = -EBADF; return d; } @@ -134,7 +136,7 @@ int write_string_stream_ts( const struct timespec *ts) { bool needs_nl; - int r, fd = -1; + int r, fd = -EBADF; assert(f); assert(line); @@ -209,7 +211,8 @@ int write_string_stream_ts( return 0; } -static int write_string_file_atomic( +static int write_string_file_atomic_at( + int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, @@ -225,7 +228,7 @@ static int write_string_file_atomic( /* Note that we'd really like to use O_TMPFILE here, but can't really, since we want replacement * semantics here, and O_TMPFILE can't offer that. i.e. rename() replaces but linkat() doesn't. */ - r = fopen_temporary(fn, &f, &p); + r = fopen_temporary_at(dir_fd, fn, &f, &p); if (r < 0) return r; @@ -237,7 +240,7 @@ static int write_string_file_atomic( if (r < 0) goto fail; - if (rename(p, fn) < 0) { + if (renameat(dir_fd, p, dir_fd, fn) < 0) { r = -errno; goto fail; } @@ -252,11 +255,12 @@ static int write_string_file_atomic( return 0; fail: - (void) unlink(p); + (void) unlinkat(dir_fd, p, 0); return r; } -int write_string_file_ts( +int write_string_file_ts_at( + int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, @@ -272,7 +276,7 @@ int write_string_file_ts( assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC))); if (flags & WRITE_STRING_FILE_MKDIR_0755) { - r = mkdir_parents(fn, 0755); + r = mkdirat_parents(dir_fd, fn, 0755); if (r < 0) return r; } @@ -280,7 +284,7 @@ int write_string_file_ts( if (flags & WRITE_STRING_FILE_ATOMIC) { assert(flags & WRITE_STRING_FILE_CREATE); - r = write_string_file_atomic(fn, line, flags, ts); + r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts); if (r < 0) goto fail; @@ -289,12 +293,12 @@ int write_string_file_ts( assert(!ts); /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */ - fd = open(fn, O_CLOEXEC|O_NOCTTY | - (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | - (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | - (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) | - (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY), - (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); + fd = openat(dir_fd, fn, O_CLOEXEC|O_NOCTTY | + (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | + (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | + (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) | + (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY), + (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); if (fd < 0) { r = -errno; goto fail; @@ -364,7 +368,7 @@ int read_one_line_file(const char *fn, char **line) { return read_line(f, LONG_LINE_MAX, line); } -int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { +int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_extra_nl) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *buf = NULL; size_t l, k; @@ -382,7 +386,7 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { if (!buf) return -ENOMEM; - r = fopen_unlocked(fn, "re", &f); + r = fopen_unlocked_at(dir_fd, fn, "re", 0, &f); if (r < 0) return r; @@ -554,7 +558,7 @@ int read_virtual_file_at( char **ret_contents, size_t *ret_size) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; assert(dir_fd >= 0 || dir_fd == AT_FDCWD); @@ -763,7 +767,7 @@ int read_full_file_full( r = xfopenat(dir_fd, filename, "re", 0, &f); if (r < 0) { - _cleanup_close_ int sk = -1; + _cleanup_close_ int sk = -EBADF; /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ if (r != -ENXIO) @@ -858,7 +862,6 @@ int executable_is_script(const char *path, char **interpreter) { int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { _cleanup_free_ char *status = NULL; char *t, *f; - size_t len; int r; assert(terminator); @@ -910,9 +913,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin t--; } - len = strcspn(t, terminator); - - f = strndup(t, len); + f = strdupcspn(t, terminator); if (!f) return -ENOMEM; @@ -1245,7 +1246,7 @@ typedef enum EndOfLineMarker { static EndOfLineMarker categorize_eol(char c, ReadLineFlags flags) { - if (!IN_SET(flags, READ_LINE_ONLY_NUL)) { + if (!FLAGS_SET(flags, READ_LINE_ONLY_NUL)) { if (c == '\n') return EOL_TEN; if (c == '\r') diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index 9151d8237a..7da3ee33e0 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -43,7 +43,10 @@ typedef enum { READ_FULL_FILE_FAIL_WHEN_LARGER = 1 << 5, /* fail loading if file is larger than specified size */ } ReadFullFileFlags; -int fopen_unlocked(const char *path, const char *options, FILE **ret); +int fopen_unlocked_at(int dir_fd, const char *path, const char *options, int flags, FILE **ret); +static inline int fopen_unlocked(const char *path, const char *options, FILE **ret) { + return fopen_unlocked_at(AT_FDCWD, path, options, 0, ret); +} int fdopen_unlocked(int fd, const char *options, FILE **ret); int take_fdopen_unlocked(int *fd, const char *options, FILE **ret); FILE* take_fdopen(int *fd, const char *options); @@ -55,7 +58,13 @@ int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { return write_string_stream_ts(f, line, flags, NULL); } -int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts); +int write_string_file_ts_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts); +static inline int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts) { + return write_string_file_ts_at(AT_FDCWD, fn, line, flags, ts); +} +static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) { + return write_string_file_ts_at(dir_fd, fn, line, flags, NULL); +} static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { return write_string_file_ts(fn, line, flags, NULL); } @@ -64,6 +73,9 @@ int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *f int read_one_line_file(const char *filename, char **line); int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size); +static inline int read_full_file_at(int dir_fd, const char *filename, char **ret_contents, size_t *ret_size) { + return read_full_file_full(dir_fd, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); +} static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) { return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); } @@ -82,7 +94,10 @@ static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_siz return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size); } -int verify_file(const char *fn, const char *blob, bool accept_extra_nl); +int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_extra_nl); +static inline int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { + return verify_file_at(AT_FDCWD, fn, blob, accept_extra_nl); +} int executable_is_script(const char *path, char **interpreter); diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 6b757bd570..a895f4f2df 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -32,7 +32,6 @@ #include "tmpfile-util.h" #include "umask-util.h" #include "user-util.h" -#include "util.h" int unlink_noerrno(const char *path) { PROTECT_ERRNO; @@ -175,37 +174,41 @@ int readlink_value(const char *p, char **ret) { return 0; } -int readlink_and_make_absolute(const char *p, char **r) { +int readlink_and_make_absolute(const char *p, char **ret) { _cleanup_free_ char *target = NULL; - char *k; - int j; + int r; assert(p); - assert(r); - - j = readlink_malloc(p, &target); - if (j < 0) - return j; + assert(ret); - k = file_in_same_dir(p, target); - if (!k) - return -ENOMEM; + r = readlink_malloc(p, &target); + if (r < 0) + return r; - *r = k; - return 0; + return file_in_same_dir(p, target, ret); } -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { - _cleanup_close_ int fd = -1; +int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid) { + _cleanup_close_ int fd = -EBADF; - assert(path); + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); - fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change - * mode/owner on the same file */ - if (fd < 0) - return -errno; + if (path) { + /* Let's acquire an O_PATH fd, as precaution to change mode/owner on the same file */ + fd = openat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + dir_fd = fd; + + } else if (dir_fd == AT_FDCWD) { + /* Let's acquire an O_PATH fd of the current directory */ + fd = openat(dir_fd, ".", O_PATH|O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY); + if (fd < 0) + return -errno; + dir_fd = fd; + } - return fchmod_and_chown(fd, mode, uid, gid); + return fchmod_and_chown(dir_fd, mode, uid, gid); } int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid) { @@ -351,7 +354,7 @@ int fd_warn_permissions(const char *path, int fd) { } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; int r, ret; assert(path); @@ -675,7 +678,7 @@ void unlink_tempfilep(char (*p)[]) { } int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { - _cleanup_close_ int truncate_fd = -1; + _cleanup_close_ int truncate_fd = -EBADF; struct stat st; off_t l, bs; @@ -806,7 +809,7 @@ int conservative_renameat( int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { - _cleanup_close_ int old_fd = -1, new_fd = -1; + _cleanup_close_ int old_fd = -EBADF, new_fd = -EBADF; struct stat old_stat, new_stat; /* Renames the old path to thew new path, much like renameat() — except if both are regular files and @@ -902,7 +905,7 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) { /* On EINTR try a couple of times more, but protect against busy looping * (not more than 16 times per 10s) */ - rl = (RateLimit) { 10 * USEC_PER_SEC, 16 }; + rl = (const RateLimit) { 10 * USEC_PER_SEC, 16 }; while (ratelimit_below(&rl)) { r = posix_fallocate(fd, offset, size); if (r != EINTR) @@ -988,7 +991,7 @@ int parse_cifs_service( } int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { - _cleanup_close_ int fd = -1, parent_fd = -1; + _cleanup_close_ int fd = -EBADF, parent_fd = -EBADF; _cleanup_free_ char *fname = NULL; bool made; int r; diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index c4dffc48f3..932d003f19 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -33,7 +33,10 @@ int readlink_malloc(const char *p, char **r); int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); -int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid); +static inline int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { + return chmod_and_chown_at(AT_FDCWD, path, mode, uid, gid); +} int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t uid, gid_t gid); static inline int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { return fchmod_and_chown_with_fallback(fd, NULL, mode, uid, gid); /* no fallback */ diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c index 67f2270daf..2833125ed9 100644 --- a/src/libnm-systemd-shared/src/basic/glyph-util.c +++ b/src/libnm-systemd-shared/src/basic/glyph-util.c @@ -9,15 +9,15 @@ bool emoji_enabled(void) { static int cached_emoji_enabled = -1; if (cached_emoji_enabled < 0) { - int val; - - val = getenv_bool("SYSTEMD_EMOJI"); - if (val < 0) - cached_emoji_enabled = - is_locale_utf8() && - !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux"); - else - cached_emoji_enabled = val; + int val = getenv_bool("SYSTEMD_EMOJI"); + if (val >= 0) + return (cached_emoji_enabled = val); + + const char *term = getenv("TERM"); + if (!term || STR_IN_SET(term, "dumb", "linux")) + return (cached_emoji_enabled = false); + + cached_emoji_enabled = is_locale_utf8(); } return cached_emoji_enabled; @@ -71,6 +71,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_RECYCLING] = "~", [SPECIAL_GLYPH_DOWNLOAD] = "\\", [SPECIAL_GLYPH_SPARKLES] = "*", + [SPECIAL_GLYPH_WARNING_SIGN] = "!", }, /* UTF-8 */ @@ -124,10 +125,11 @@ const char *special_glyph(SpecialGlyph code) { /* This emoji is a single character cell glyph in Unicode, and two in ASCII */ [SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */ - /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */ + /* These four emojis are single character cell glyphs in Unicode and also in ASCII. */ [SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */ [SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */ [SPECIAL_GLYPH_SPARKLES] = u8"✨", + [SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️", }, }; diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h index 621d7a85b7..b64639622e 100644 --- a/src/libnm-systemd-shared/src/basic/glyph-util.h +++ b/src/libnm-systemd-shared/src/basic/glyph-util.h @@ -44,6 +44,7 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_RECYCLING, SPECIAL_GLYPH_DOWNLOAD, SPECIAL_GLYPH_SPARKLES, + SPECIAL_GLYPH_WARNING_SIGN, _SPECIAL_GLYPH_MAX, _SPECIAL_GLYPH_INVALID = -EINVAL, } SpecialGlyph; diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index f68cd36bb7..322b148b31 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "fileio.h" #include "hashmap.h" +#include "logarithm.h" #include "macro.h" #include "memory-util.h" #include "mempool.h" @@ -372,8 +373,9 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { } static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) - ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); + return CAST_ALIGN_PTR( + struct hashmap_base_entry, + (uint8_t *) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); } static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) { @@ -772,7 +774,7 @@ static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enu HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool use_pool = mempool_enabled && mempool_enabled(); + bool use_pool = mempool_enabled && mempool_enabled(); /* mempool_enabled is a weak symbol */ h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); if (!h) @@ -1751,7 +1753,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) { } if (r < 0) - return _hashmap_free(copy, false, false); + return _hashmap_free(copy, NULL, NULL); return copy; } diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h index 91b3fe862b..ebb5a63eb4 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.h +++ b/src/libnm-systemd-shared/src/basic/hashmap.h @@ -7,7 +7,6 @@ #include "hash-funcs.h" #include "macro.h" -#include "util.h" /* * A hash table implementation. As a minor optimization a NULL hashmap object diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c index 190fca8ee4..898ed83f86 100644 --- a/src/libnm-systemd-shared/src/basic/hexdecoct.c +++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c @@ -59,11 +59,13 @@ char *hexmem(const void *p, size_t l) { const uint8_t *x; char *r, *z; + assert(p || l == 0); + z = r = new(char, l * 2 + 1); if (!r) return NULL; - for (x = p; x < (const uint8_t*) p + l; x++) { + for (x = p; x && x < (const uint8_t*) p + l; x++) { *(z++) = hexchar(*x >> 4); *(z++) = hexchar(*x & 15); } @@ -108,12 +110,17 @@ static int unhex_next(const char **p, size_t *l) { return r; } -int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_len) { +int unhexmem_full( + const char *p, + size_t l, + bool secure, + void **ret, + size_t *ret_len) { + _cleanup_free_ uint8_t *buf = NULL; size_t buf_size; const char *x; uint8_t *z; - int r; assert(p || l == 0); @@ -126,22 +133,20 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ if (!buf) return -ENOMEM; + CLEANUP_ERASE_PTR(secure ? &buf : NULL, buf_size); + for (x = p, z = buf;;) { int a, b; a = unhex_next(&x, &l); if (a == -EPIPE) /* End of string */ break; - if (a < 0) { - r = a; - goto on_failure; - } + if (a < 0) + return a; b = unhex_next(&x, &l); - if (b < 0) { - r = b; - goto on_failure; - } + if (b < 0) + return b; *(z++) = (uint8_t) a << 4 | (uint8_t) b; } @@ -154,12 +159,6 @@ int unhexmem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_ *ret = TAKE_PTR(buf); return 0; - -on_failure: - if (secure) - explicit_bzero_safe(buf, buf_size); - - return r; } /* https://tools.ietf.org/html/rfc4648#section-6 @@ -586,121 +585,136 @@ ssize_t base64mem_full( const void *p, size_t l, size_t line_break, - char **out) { + char **ret) { const uint8_t *x; - char *r, *z; + char *b, *z; size_t m; assert(p || l == 0); - assert(out); assert(line_break > 0); + assert(ret); /* three input bytes makes four output bytes, padding is added so we must round up */ m = 4 * (l + 2) / 3 + 1; - if (line_break != SIZE_MAX) m += m / line_break; - z = r = malloc(m); - if (!r) + z = b = malloc(m); + if (!b) return -ENOMEM; - for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { + for (x = p; x && x < (const uint8_t*) p + (l / 3) * 3; x += 3) { /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ } switch (l % 3) { case 2: - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = '='; - break; + case 1: - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = '='; - maybe_line_break(&z, r, line_break); + maybe_line_break(&z, b, line_break); *(z++) = '='; - break; } *z = 0; - *out = r; - assert(z >= r); /* Let static analyzers know that the answer is non-negative. */ - return z - r; + *ret = b; + + assert(z >= b); /* Let static analyzers know that the answer is non-negative. */ + return z - b; } -static int base64_append_width( - char **prefix, int plen, - char sep, int indent, - const void *p, size_t l, - int width) { +static ssize_t base64_append_width( + char **prefix, + size_t plen, + char sep, + size_t indent, + const void *p, + size_t l, + size_t width) { _cleanup_free_ char *x = NULL; char *t, *s; - ssize_t len, avail, line, lines; + size_t lines; + ssize_t len; + + assert(prefix); + assert(*prefix || plen == 0); + assert(p || l == 0); len = base64mem(p, l, &x); - if (len <= 0) + if (len < 0) return len; + if (len == 0) + return plen; lines = DIV_ROUND_UP(len, width); - if ((size_t) plen >= SSIZE_MAX - 1 - 1 || + if (plen >= SSIZE_MAX - 1 - 1 || lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1)) return -ENOMEM; - t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines); + t = realloc(*prefix, plen + 1 + 1 + (indent + width + 1) * lines); if (!t) return -ENOMEM; - t[plen] = sep; + s = t + plen; + for (size_t line = 0; line < lines; line++) { + size_t act = MIN(width, (size_t) len); - for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) { - int act = MIN(width, avail); + if (line > 0) + sep = '\n'; - if (line > 0 || sep == '\n') { - memset(s, ' ', indent); - s += indent; + if (s > t) { + *s++ = sep; + if (sep == '\n') + s = mempset(s, ' ', indent); } s = mempcpy(s, x + width * line, act); - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; + len -= act; } - assert(avail == 0); + assert(len == 0); + *s = '\0'; *prefix = t; - return 0; + return s - t; } -int base64_append( - char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { +ssize_t base64_append( + char **prefix, + size_t plen, + const void *p, + size_t l, + size_t indent, + size_t width) { if (plen > width / 2 || plen + indent > width) /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1); + return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent); else /* leave plen on the left, keep last column free */ return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1); @@ -748,12 +762,17 @@ static int unbase64_next(const char **p, size_t *l) { return ret; } -int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) { +int unbase64mem_full( + const char *p, + size_t l, + bool secure, + void **ret, + size_t *ret_size) { + _cleanup_free_ uint8_t *buf = NULL; const char *x; uint8_t *z; size_t len; - int r; assert(p || l == 0); @@ -768,60 +787,44 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r if (!buf) return -ENOMEM; + CLEANUP_ERASE_PTR(secure ? &buf : NULL, len); + for (x = p, z = buf;;) { int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ a = unbase64_next(&x, &l); if (a == -EPIPE) /* End of string */ break; - if (a < 0) { - r = a; - goto on_failure; - } - if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ - r = -EINVAL; - goto on_failure; - } + if (a < 0) + return a; + if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ + return -EINVAL; b = unbase64_next(&x, &l); - if (b < 0) { - r = b; - goto on_failure; - } - if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ - r = -EINVAL; - goto on_failure; - } + if (b < 0) + return b; + if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ + return -EINVAL; c = unbase64_next(&x, &l); - if (c < 0) { - r = c; - goto on_failure; - } + if (c < 0) + return c; d = unbase64_next(&x, &l); - if (d < 0) { - r = d; - goto on_failure; - } + if (d < 0) + return d; if (c == INT_MAX) { /* Padding at the third character */ - if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ - r = -EINVAL; - goto on_failure; - } + if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ + return -EINVAL; /* b == 00YY0000 */ - if (b & 15) { - r = -EINVAL; - goto on_failure; - } + if (b & 15) + return -EINVAL; - if (l > 0) { /* Trailing rubbish? */ - r = -ENAMETOOLONG; - goto on_failure; - } + if (l > 0) /* Trailing rubbish? */ + return -ENAMETOOLONG; *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ break; @@ -829,15 +832,11 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r if (d == INT_MAX) { /* c == 00ZZZZ00 */ - if (c & 3) { - r = -EINVAL; - goto on_failure; - } + if (c & 3) + return -EINVAL; - if (l > 0) { /* Trailing rubbish? */ - r = -ENAMETOOLONG; - goto on_failure; - } + if (l > 0) /* Trailing rubbish? */ + return -ENAMETOOLONG; *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ @@ -851,18 +850,14 @@ int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *r *z = 0; + assert((size_t) (z - buf) <= len); + if (ret_size) *ret_size = (size_t) (z - buf); if (ret) *ret = TAKE_PTR(buf); return 0; - -on_failure: - if (secure) - explicit_bzero_safe(buf, len); - - return r; } void hexdump(FILE *f, const void *p, size_t s) { diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h index 5218f78665..319b21a17c 100644 --- a/src/libnm-systemd-shared/src/basic/hexdecoct.h +++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h @@ -38,9 +38,13 @@ static inline ssize_t base64mem(const void *p, size_t l, char **ret) { return base64mem_full(p, l, SIZE_MAX, ret); } -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); +ssize_t base64_append( + char **prefix, + size_t plen, + const void *p, + size_t l, + size_t margin, + size_t width); int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) { return unbase64mem_full(p, l, false, mem, len); diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index b710f07929..e743033b1e 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -62,7 +62,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) { } if (FLAGS_SET(flags, GET_HOSTNAME_SHORT)) - buf = strndup(s, strcspn(s, ".")); + buf = strdupcspn(s, "."); else buf = strdup(s); if (!buf) diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h index a00b852395..bcac3d9fb0 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -60,4 +60,12 @@ static inline bool is_outbound_hostname(const char *hostname) { return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); } +static inline bool is_dns_stub_hostname(const char *hostname) { + return STRCASE_IN_SET(hostname, "_localdnsstub", "_localdnsstub."); +} + +static inline bool is_dns_proxy_stub_hostname(const char *hostname) { + return STRCASE_IN_SET(hostname, "_localdnsproxy", "_localdnsproxy."); +} + int get_pretty_hostname(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 05c729d34d..30d90cce0d 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -11,13 +11,13 @@ #include "alloc-util.h" #include "errno-util.h" #include "in-addr-util.h" +#include "logarithm.h" #include "macro.h" #include "parse-util.h" #include "random-util.h" #include "stdio-util.h" #include "string-util.h" #include "strxcpyx.h" -#include "util.h" bool in4_addr_is_null(const struct in_addr *a) { assert(a); @@ -898,14 +898,6 @@ int in_addr_prefix_from_string_auto_internal( break; case PREFIXLEN_REFUSE: return -ENOANO; /* To distinguish this error from others. */ - case PREFIXLEN_LEGACY: - if (family == AF_INET) { - r = in4_addr_default_prefixlen(&buffer.in, &k); - if (r < 0) - return r; - } else - k = 0; - break; default: assert_not_reached(); } @@ -921,7 +913,7 @@ int in_addr_prefix_from_string_auto_internal( } -static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) { +void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) { assert(a); assert(state); @@ -929,7 +921,7 @@ static void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); } -static int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) { +int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) { int r; assert(x); @@ -942,7 +934,18 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); } -DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func); +DEFINE_HASH_OPS( + in_addr_data_hash_ops, + struct in_addr_data, + in_addr_data_hash_func, + in_addr_data_compare_func); + +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( + in_addr_data_hash_ops_free, + struct in_addr_data, + in_addr_data_hash_func, + in_addr_data_compare_func, + free); void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { assert(addr); @@ -958,7 +961,12 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) { return memcmp(a, b, sizeof(*a)); } -DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func); +DEFINE_HASH_OPS( + in6_addr_hash_ops, + struct in6_addr, + in6_addr_hash_func, + in6_addr_compare_func); + DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( in6_addr_hash_ops_free, struct in6_addr, 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 19fa35f1d2..200b9eb69d 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -8,7 +8,6 @@ #include "hash-funcs.h" #include "macro.h" -#include "util.h" union in_addr_union { struct in_addr in; @@ -154,7 +153,6 @@ int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *r typedef enum InAddrPrefixLenMode { PREFIXLEN_FULL, /* Default to prefixlen of address size, 32 for IPv4 or 128 for IPv6, if not specified. */ PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */ - PREFIXLEN_LEGACY, /* Default to legacy default prefixlen calculation from address if not specified. */ } InAddrPrefixLenMode; int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen); @@ -178,10 +176,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) { * See also oss-fuzz#11344. */ #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} }) +void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state); +int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y); void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state); int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b); extern const struct hash_ops in_addr_data_hash_ops; +extern const struct hash_ops in_addr_data_hash_ops_free; extern const struct hash_ops in6_addr_hash_ops; extern const struct hash_ops in6_addr_hash_ops_free; diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index cdad939aa6..f642beca3a 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -161,6 +161,21 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { assert(fds || nfds == 0); + /* This is a wrapper around ppoll() that does primarily two things: + * + * ✅ Takes a usec_t instead of a struct timespec + * + * ✅ Guarantees that if an invalid fd is specified we return EBADF (i.e. converts POLLNVAL to + * EBADF). This is done because EBADF is a programming error usually, and hence should bubble up + * as error, and not be eaten up as non-error POLLNVAL event. + * + * ⚠️ ⚠️ ⚠️ Note that this function does not add any special handling for EINTR. Don't forget + * poll()/ppoll() will return with EINTR on any received signal always, there is no automatic + * restarting via SA_RESTART available. Thus, typically you want to handle EINTR not as an error, + * but just as reason to restart things, under the assumption you use a more appropriate mechanism + * to handle signals, such as signalfd() or signal handlers. ⚠️ ⚠️ ⚠️ + */ + if (nfds == 0) return 0; @@ -188,6 +203,9 @@ int fd_wait_for_event(int fd, int event, usec_t timeout) { }; int r; + /* ⚠️ ⚠️ ⚠️ Keep in mind you almost certainly want to handle -EINTR gracefully in the caller, see + * ppoll_usec() above! ⚠️ ⚠️ ⚠️ */ + r = ppoll_usec(&pollfd, 1, timeout); if (r <= 0) return r; diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h index 39728e06bc..3afb134266 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.h +++ b/src/libnm-systemd-shared/src/basic/io-util.h @@ -91,7 +91,16 @@ struct iovec_wrapper *iovw_new(void); struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw); struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw); void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors); + int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len); +static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) { + /* Move data into iovw or free on error */ + int r = iovw_put(iovw, data, len); + if (r < 0) + free(data); + return r; +} + int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value); int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value); void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h index ca30039690..ffc8bd8304 100644 --- a/src/libnm-systemd-shared/src/basic/list.h +++ b/src/libnm-systemd-shared/src/basic/list.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "macro.h" - /* The head of the linked list. Use this in the structure that shall * contain the head of the linked list */ #define LIST_HEAD(t,name) \ @@ -28,26 +26,27 @@ /* Prepend an item to the list */ #define LIST_PREPEND(name,head,item) \ - do { \ + ({ \ typeof(*(head)) **_head = &(head), *_item = (item); \ assert(_item); \ if ((_item->name##_next = *_head)) \ _item->name##_next->name##_prev = _item; \ _item->name##_prev = NULL; \ *_head = _item; \ - } while (false) + _item; \ + }) /* Append an item to the list */ #define LIST_APPEND(name,head,item) \ - do { \ + ({ \ typeof(*(head)) **_hhead = &(head), *_tail; \ - LIST_FIND_TAIL(name, *_hhead, _tail); \ + _tail = LIST_FIND_TAIL(name, *_hhead); \ LIST_INSERT_AFTER(name, *_hhead, _tail, item); \ - } while (false) + }) /* Remove an item from the list */ #define LIST_REMOVE(name,head,item) \ - do { \ + ({ \ typeof(*(head)) **_head = &(head), *_item = (item); \ assert(_item); \ if (_item->name##_next) \ @@ -59,37 +58,30 @@ *_head = _item->name##_next; \ } \ _item->name##_next = _item->name##_prev = NULL; \ - } while (false) + _item; \ + }) /* Find the head of the list */ -#define LIST_FIND_HEAD(name,item,head) \ - do { \ +#define LIST_FIND_HEAD(name,item) \ + ({ \ typeof(*(item)) *_item = (item); \ - if (!_item) \ - (head) = NULL; \ - else { \ - while (_item->name##_prev) \ - _item = _item->name##_prev; \ - (head) = _item; \ - } \ - } while (false) + while (_item && _item->name##_prev) \ + _item = _item->name##_prev; \ + _item; \ + }) /* Find the tail of the list */ -#define LIST_FIND_TAIL(name,item,tail) \ - do { \ +#define LIST_FIND_TAIL(name,item) \ + ({ \ typeof(*(item)) *_item = (item); \ - if (!_item) \ - (tail) = NULL; \ - else { \ - while (_item->name##_next) \ - _item = _item->name##_next; \ - (tail) = _item; \ - } \ - } while (false) + while (_item && _item->name##_next) \ + _item = _item->name##_next; \ + _item; \ + }) /* Insert an item after another one (a = where, b = what) */ #define LIST_INSERT_AFTER(name,head,a,b) \ - do { \ + ({ \ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ assert(_b); \ if (!_a) { \ @@ -103,11 +95,12 @@ _b->name##_prev = _a; \ _a->name##_next = _b; \ } \ - } while (false) + _b; \ + }) /* Insert an item before another one (a = where, b = what) */ #define LIST_INSERT_BEFORE(name,head,a,b) \ - do { \ + ({ \ typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ assert(_b); \ if (!_a) { \ @@ -131,7 +124,8 @@ _b->name##_next = _a; \ _a->name##_prev = _b; \ } \ - } while (false) + _b; \ + }) #define LIST_JUST_US(name,item) \ (!(item)->name##_prev && !(item)->name##_next) @@ -172,18 +166,19 @@ /* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */ #define LIST_JOIN(name,a,b) \ - do { \ + ({ \ assert(b); \ if (!(a)) \ (a) = (b); \ else { \ typeof(*(a)) *_head = (b), *_tail; \ - LIST_FIND_TAIL(name, (a), _tail); \ + _tail = LIST_FIND_TAIL(name, (a)); \ _tail->name##_next = _head; \ _head->name##_prev = _tail; \ } \ (b) = NULL; \ - } while (false) + a; \ + }) #define LIST_POP(name, a) \ ({ \ @@ -193,3 +188,7 @@ LIST_REMOVE(name, *_a, _p); \ _p; \ }) + +/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include + * it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */ +#include "macro.h" diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c index d8518ec06a..d94fbcff4b 100644 --- a/src/libnm-systemd-shared/src/basic/locale-util.c +++ b/src/libnm-systemd-shared/src/basic/locale-util.c @@ -10,7 +10,7 @@ #include <sys/mman.h> #include <sys/stat.h> -#include "def.h" +#include "constants.h" #include "dirent-util.h" #include "env-util.h" #include "fd-util.h" @@ -95,7 +95,7 @@ static int add_locales_from_archive(Set *locales) { const struct locarhead *h; const struct namehashent *e; const void *p = MAP_FAILED; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; size_t sz = 0; struct stat st; int r; @@ -286,8 +286,9 @@ void init_gettext(void) { } bool is_locale_utf8(void) { - const char *set; static int cached_answer = -1; + const char *set; + int r; /* Note that we default to 'true' here, since today UTF8 is * pretty much supported everywhere. */ @@ -295,6 +296,13 @@ bool is_locale_utf8(void) { if (cached_answer >= 0) goto out; + r = getenv_bool_secure("SYSTEMD_UTF8"); + if (r >= 0) { + cached_answer = r; + goto out; + } else if (r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m"); + if (!setlocale(LC_ALL, "")) { cached_answer = true; goto out; diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index c51941c141..f73d4c4154 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -7,8 +7,10 @@ #include <string.h> #include <syslog.h> +#include "list.h" #include "macro.h" #include "ratelimit.h" +#include "stdio-util.h" /* Some structures we reference but don't want to pull in headers for */ struct iovec; @@ -296,6 +298,7 @@ int log_emergency_level(void); #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__) +#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__) bool log_on_console(void) _pure_; @@ -375,15 +378,12 @@ typedef struct LogRateLimit { RateLimit ratelimit; } LogRateLimit; -#define log_ratelimit_internal(_level, _error, _format, _file, _line, _func, ...) \ +#define log_ratelimit_internal(_level, _error, _ratelimit, _format, _file, _line, _func, ...) \ ({ \ int _log_ratelimit_error = (_error); \ int _log_ratelimit_level = (_level); \ static LogRateLimit _log_ratelimit = { \ - .ratelimit = { \ - .interval = 1 * USEC_PER_SEC, \ - .burst = 1, \ - }, \ + .ratelimit = (_ratelimit), \ }; \ unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \ if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \ @@ -391,18 +391,115 @@ typedef struct LogRateLimit { _log_ratelimit.error = _log_ratelimit_error; \ _log_ratelimit.level = _log_ratelimit_level; \ } \ - if (ratelimit_below(&_log_ratelimit.ratelimit)) \ + if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \ _log_ratelimit_error = _num_dropped_errors > 0 \ - ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", __VA_ARGS__, _num_dropped_errors) \ - : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, __VA_ARGS__); \ + ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \ + : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \ _log_ratelimit_error; \ }) -#define log_ratelimit_full_errno(level, error, format, ...) \ +#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...) \ ({ \ int _level = (level), _e = (error); \ _e = (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_ratelimit_internal(_level, _e, format, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ + ? log_ratelimit_internal(_level, _e, _ratelimit, format, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__) \ : -ERRNO_VALUE(_e); \ _e < 0 ? _e : -ESTRPIPE; \ }) + +#define log_ratelimit_full(level, _ratelimit, format, ...) \ + log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__) + +/* Normal logging */ +#define log_ratelimit_info(...) log_ratelimit_full(LOG_INFO, __VA_ARGS__) +#define log_ratelimit_notice(...) log_ratelimit_full(LOG_NOTICE, __VA_ARGS__) +#define log_ratelimit_warning(...) log_ratelimit_full(LOG_WARNING, __VA_ARGS__) +#define log_ratelimit_error(...) log_ratelimit_full(LOG_ERR, __VA_ARGS__) +#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__) + +/* Logging triggered by an errno-like error */ +#define log_ratelimit_info_errno(error, ...) log_ratelimit_full_errno(LOG_INFO, error, __VA_ARGS__) +#define log_ratelimit_notice_errno(error, ...) log_ratelimit_full_errno(LOG_NOTICE, error, __VA_ARGS__) +#define log_ratelimit_warning_errno(error, ...) log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__) +#define log_ratelimit_error_errno(error, ...) log_ratelimit_full_errno(LOG_ERR, error, __VA_ARGS__) +#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__) + +/* + * The log context allows attaching extra metadata to log messages written to the journal via log.h. We keep + * track of a thread local log context onto which we can push extra metadata fields that should be logged. + * + * LOG_CONTEXT_PUSH() will add the provided field to the log context and will remove it again when the + * current block ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in the given strv. + * LOG_CONTEXT_PUSHF() is like LOG_CONTEXT_PUSH() but takes a format string and arguments. + * + * Using the macros is as simple as putting them anywhere inside a block to add a field to all following log + * messages logged from inside that block. + * + * void myfunction(...) { + * ... + * + * LOG_CONTEXT_PUSHF("MYMETADATA=%s", "abc"); + * + * // Every journal message logged will now have the MYMETADATA=abc + * // field included. + * } + * + * One special case to note is async code, where we use callbacks that are invoked to continue processing + * when some event occurs. For async code, there's usually an associated "userdata" struct containing all the + * information associated with the async operation. In this "userdata" struct, we can store a log context + * allocated with log_context_new() and freed with log_context_free(). We can then add and remove fields to + * the `fields` member of the log context object and all those fields will be logged along with each log + * message. + */ + +typedef struct LogContext LogContext; + +bool log_context_enabled(void); + +LogContext* log_context_attach(LogContext *c); +LogContext* log_context_detach(LogContext *c); + +LogContext* log_context_new(char **fields, bool owned); +LogContext* log_context_free(LogContext *c); + +/* Same as log_context_new(), but frees the given fields strv on failure. */ +LogContext* log_context_new_consume(char **fields); + +/* Returns the number of attached log context objects. */ +size_t log_context_num_contexts(void); +/* Returns the number of fields in all attached log contexts. */ +size_t log_context_num_fields(void); + +DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_detach); +DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_free); + +#define LOG_CONTEXT_PUSH(...) \ + LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__)) + +#define LOG_CONTEXT_PUSHF(...) \ + LOG_CONTEXT_PUSH(snprintf_ok((char[LINE_MAX]) {}, LINE_MAX, __VA_ARGS__)) + +#define _LOG_CONTEXT_PUSH_STRV(strv, c) \ + _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new(strv, /*owned=*/ false); + +#define LOG_CONTEXT_PUSH_STRV(strv) \ + _LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ)) + +/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV() are identical to + * LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV() except they take ownership of the given str/strv argument. + */ + +#define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \ + _unused_ _cleanup_strv_free_ strv = strv_new(s); \ + if (!strv) \ + free(s); \ + _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new_consume(TAKE_PTR(strv)) + +#define LOG_CONTEXT_CONSUME_STR(s) \ + _LOG_CONTEXT_CONSUME_STR(s, UNIQ_T(c, UNIQ), UNIQ_T(sv, UNIQ)) + +#define _LOG_CONTEXT_CONSUME_STRV(strv, c) \ + _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new_consume(strv); + +#define LOG_CONTEXT_CONSUME_STRV(strv) \ + _LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ)) diff --git a/src/libnm-systemd-shared/src/basic/util.h b/src/libnm-systemd-shared/src/basic/logarithm.h index 68ae3b51e0..646f2d3613 100644 --- a/src/libnm-systemd-shared/src/basic/util.h +++ b/src/libnm-systemd-shared/src/basic/logarithm.h @@ -5,27 +5,6 @@ #include "macro.h" -extern int saved_argc; -extern char **saved_argv; - -static inline void save_argc_argv(int argc, char **argv) { - - /* Protect against CVE-2021-4034 style attacks */ - assert_se(argc > 0); - assert_se(argv); - assert_se(argv[0]); - - saved_argc = argc; - saved_argv = argv; -} - -bool kexec_loaded(void); - -int prot_from_flags(int flags) _const_; - -bool in_initrd(void); -void in_initrd_force(bool value); - /* Note: log2(0) == log2(1) == 0 here and below. */ #define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0) @@ -72,9 +51,3 @@ static inline unsigned log2u_round_up(unsigned x) { return log2u(x - 1) + 1; } - -int container_get_leader(const char *machine, pid_t *pid); - -int version(void); - -void disable_coredumps(void); diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index 237117db12..0d032866cf 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -9,32 +9,9 @@ #include <sys/sysmacros.h> #include <sys/types.h> +#include "constants.h" #include "macro-fundamental.h" -#if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# if defined(__has_feature) -# if __has_feature(memory_sanitizer) -# define HAS_FEATURE_MEMORY_SANITIZER 1 -# endif -# endif -# if !defined(HAS_FEATURE_MEMORY_SANITIZER) -# define HAS_FEATURE_MEMORY_SANITIZER 0 -# endif -#endif - -#if !defined(HAS_FEATURE_ADDRESS_SANITIZER) -# ifdef __SANITIZE_ADDRESS__ -# define HAS_FEATURE_ADDRESS_SANITIZER 1 -# elif defined(__has_feature) -# if __has_feature(address_sanitizer) -# define HAS_FEATURE_ADDRESS_SANITIZER 1 -# endif -# endif -# if !defined(HAS_FEATURE_ADDRESS_SANITIZER) -# define HAS_FEATURE_ADDRESS_SANITIZER 0 -# endif -#endif - /* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global * variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since * our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be @@ -87,10 +64,14 @@ _Pragma("GCC diagnostic push") #endif -#define DISABLE_WARNING_TYPE_LIMITS \ +#define DISABLE_WARNING_TYPE_LIMITS \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") +#define DISABLE_WARNING_ADDRESS \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Waddress\"") + #define REENABLE_WARNING \ _Pragma("GCC diagnostic pop") @@ -194,12 +175,12 @@ static inline int __coverity_check_and_return__(int condition) { #define assert_message_se(expr, message) \ do { \ if (_unlikely_(!(expr))) \ - log_assert_failed(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \ + log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \ } while (false) #define assert_log(expr, message) ((_likely_(expr)) \ ? (true) \ - : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__), false)) + : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false)) #endif /* __COVERITY__ */ @@ -214,7 +195,7 @@ static inline int __coverity_check_and_return__(int condition) { #endif #define assert_not_reached() \ - log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__) + log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__) #define assert_return(expr, r) \ do { \ @@ -341,10 +322,14 @@ static inline int __coverity_check_and_return__(int condition) { *p = func(*p); \ } -/* When func() doesn't return the appropriate type, set variable to empty afterwards */ +/* When func() doesn't return the appropriate type, set variable to empty afterwards. + * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */ #define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \ static inline void func##p(type *p) { \ if (*p != (empty)) { \ + DISABLE_WARNING_ADDRESS; \ + assert(func); \ + REENABLE_WARNING; \ func(*p); \ *p = (empty); \ } \ diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c index 2983762117..c4f54c7b4e 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.c +++ b/src/libnm-systemd-shared/src/basic/memory-util.c @@ -38,21 +38,3 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) { /* Now we know first 16 bytes match, memcmp() with self. */ return memcmp(data, p + 16, length) == 0; } - -#if !HAVE_EXPLICIT_BZERO -/* - * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that - * it points to any function in particular (such as memset(), which it then might further "optimize"). This - * approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - memset_func(p, '\0', l); - - return p; -} -#endif diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h index 6e3280b9df..428ccc210c 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.h +++ b/src/libnm-systemd-shared/src/basic/memory-util.h @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "macro.h" +#include "memory-util-fundamental.h" size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) @@ -91,17 +92,6 @@ static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const return (uint8_t*) p + needlelen; } -#if HAVE_EXPLICIT_BZERO -static inline void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - explicit_bzero(p, l); - - return p; -} -#else -void *explicit_bzero_safe(void *p, size_t l); -#endif - static inline void* erase_and_free(void *p) { size_t l; diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index 793d111c55..98cd037962 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -363,6 +363,20 @@ static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) /* ======================================================================= */ +#if !HAVE_RT_TGSIGQUEUEINFO +static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) { +# if defined __NR_rt_tgsigqueueinfo && __NR_rt_tgsigqueueinfo >= 0 + return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info); +# else +# error "__NR_rt_tgsigqueueinfo not defined" +# endif +} + +# define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo +#endif + +/* ======================================================================= */ + #if !HAVE_EXECVEAT static inline int missing_execveat(int dirfd, const char *pathname, char *const argv[], char *const envp[], @@ -412,44 +426,6 @@ static inline int missing_close_range(int first_fd, int end_fd, unsigned flags) /* ======================================================================= */ -#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 - -/* ======================================================================= */ - #if !HAVE_MOUNT_SETATTR #if !HAVE_STRUCT_MOUNT_ATTR @@ -593,6 +569,46 @@ static inline int missing_move_mount( /* ======================================================================= */ +#if !HAVE_FSOPEN + +#ifndef FSOPEN_CLOEXEC +#define FSOPEN_CLOEXEC 0x00000001 +#endif + +static inline int missing_fsopen(const char *fsname, unsigned flags) { +# if defined __NR_fsopen && __NR_fsopen >= 0 + return syscall(__NR_fsopen, fsname, flags); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define fsopen missing_fsopen +#endif + +/* ======================================================================= */ + +#if !HAVE_FSCONFIG + +#ifndef FSCONFIG_SET_STRING +#define FSCONFIG_SET_STRING 1 /* Set parameter, supplying a string value */ +#endif + +static inline int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) { +# if defined __NR_fsconfig && __NR_fsconfig >= 0 + return syscall(__NR_fsconfig, fd, cmd, key, value, aux); +# else + errno = ENOSYS; + return -1; +# endif +} + +# define fsconfig missing_fsconfig +#endif + +/* ======================================================================= */ + #if !HAVE_GETDENTS64 static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) { diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index 3b3efb0ab8..3445d31307 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -256,6 +256,26 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) { return 0; } +int parse_sector_size(const char *t, uint64_t *ret) { + int r; + + assert(t); + assert(ret); + + uint64_t ss; + + r = safe_atou64(t, &ss); + if (r < 0) + return log_error_errno(r, "Failed to parse sector size parameter %s", t); + if (ss < 512 || ss > 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */ + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t); + if (!ISPOWEROF2(ss)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t); + + *ret = ss; + return 0; +} + int parse_range(const char *t, unsigned *lower, unsigned *upper) { _cleanup_free_ char *word = NULL; unsigned l, u; diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index 8d8d52327b..877199529d 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -18,6 +18,7 @@ int parse_ifindex(const char *s); 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_sector_size(const char *t, uint64_t *ret); int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index bf93990fde..40a819d47d 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -519,17 +519,17 @@ char* path_extend_internal(char **x, ...) { va_list ap; bool slash; - /* Joins all listed strings until the sentinel and places a "/" between them unless the strings end/begin - * already with one so that it is unnecessary. Note that slashes which are already duplicate won't be - * removed. The string returned is hence always equal to or longer than the sum of the lengths of each - * individual string. + /* Joins all listed strings until the sentinel and places a "/" between them unless the strings + * end/begin already with one so that it is unnecessary. Note that slashes which are already + * duplicate won't be removed. The string returned is hence always equal to or longer than the sum of + * the lengths of the individual strings. * * The first argument may be an already allocated string that is extended via realloc() if * non-NULL. path_extend() and path_join() are macro wrappers around this function, making use of the * first parameter to distinguish the two operations. * - * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of which some - * are optional. + * Note: any listed empty string is simply skipped. This can be useful for concatenating strings of + * which some are optional. * * Examples: * @@ -587,7 +587,7 @@ char* path_extend_internal(char **x, ...) { } static int check_x_access(const char *path, int *ret_fd) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; int r; /* We need to use O_PATH because there may be executables for which we have only exec @@ -615,7 +615,7 @@ static int check_x_access(const char *path, int *ret_fd) { } static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; _cleanup_free_ char *path_name = NULL; int r; @@ -1164,31 +1164,35 @@ bool path_is_normalized(const char *p) { return true; } -char *file_in_same_dir(const char *path, const char *filename) { - char *e, *ret; - size_t k; +int file_in_same_dir(const char *path, const char *filename, char **ret) { + _cleanup_free_ char *b = NULL; + int r; assert(path); assert(filename); + assert(ret); - /* This removes the last component of path and appends - * filename, unless the latter is absolute anyway or the - * former isn't */ + /* This removes the last component of path and appends filename, unless the latter is absolute anyway + * or the former isn't */ if (path_is_absolute(filename)) - return strdup(filename); - - e = strrchr(path, '/'); - if (!e) - return strdup(filename); + b = strdup(filename); + else { + _cleanup_free_ char *dn = NULL; - k = strlen(filename); - ret = new(char, (e + 1 - path) + k + 1); - if (!ret) - return NULL; + r = path_extract_directory(path, &dn); + if (r == -EDESTADDRREQ) /* no path prefix */ + b = strdup(filename); + else if (r < 0) + return r; + else + b = path_join(dn, filename); + } + if (!b) + return -ENOMEM; - memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); - return ret; + *ret = TAKE_PTR(b); + return 0; } bool hidden_or_backup_file(const char *filename) { diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index 22d3632e6e..56f01f41d8 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -130,6 +130,7 @@ int fsck_exists_for_fstype(const char *fstype); /* Similar to path_join(), but only works for two components, and only the first one may be NULL and returns * an alloca() buffer, or possibly a const pointer into the path parameter. */ +/* DEPRECATED: use path_join() instead */ #define prefix_roota(root, path) \ ({ \ const char* _path = (path), *_root = (root), *_ret; \ @@ -169,7 +170,7 @@ static inline bool path_is_safe(const char *p) { } bool path_is_normalized(const char *p) _pure_; -char *file_in_same_dir(const char *path, const char *filename); +int file_in_same_dir(const char *path, const char *filename, char **ret); bool hidden_or_backup_file(const char *filename) _pure_; diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c index c15dcb26af..7e33561688 100644 --- a/src/libnm-systemd-shared/src/basic/prioq.c +++ b/src/libnm-systemd-shared/src/basic/prioq.c @@ -253,7 +253,7 @@ int prioq_remove(Prioq *q, void *data, unsigned *idx) { return 1; } -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { +void prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { struct prioq_item *i; unsigned k; @@ -261,12 +261,11 @@ int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { i = find_item(q, data, idx); if (!i) - return 0; + return; k = i - q->items; k = shuffle_down(q, k); shuffle_up(q, k); - return 1; } void *prioq_peek_by_index(Prioq *q, unsigned idx) { diff --git a/src/libnm-systemd-shared/src/basic/prioq.h b/src/libnm-systemd-shared/src/basic/prioq.h index 508db88026..f66562f387 100644 --- a/src/libnm-systemd-shared/src/basic/prioq.h +++ b/src/libnm-systemd-shared/src/basic/prioq.h @@ -18,7 +18,7 @@ int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); int prioq_put(Prioq *q, void *data, unsigned *idx); int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx); int prioq_remove(Prioq *q, void *data, unsigned *idx); -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); +void prioq_reshuffle(Prioq *q, void *data, unsigned *idx); void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_; static inline void *prioq_peek(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 fb0b38fa49..70aa15f060 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -8,7 +8,6 @@ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <sys/mman.h> #include <sys/mount.h> #include <sys/personality.h> #include <sys/prctl.h> @@ -22,19 +21,25 @@ #include "alloc-util.h" #include "architecture.h" +#include "argv-util.h" +#include "env-file.h" #include "env-util.h" #include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hostname-util.h" #include "locale-util.h" #include "log.h" #include "macro.h" #include "memory-util.h" #include "missing_sched.h" #include "missing_syscall.h" +#include "mountpoint-util.h" #include "namespace-util.h" +#include "nulstr-util.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "raw-clone.h" @@ -253,149 +258,45 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags return 0; } -static int update_argv(const char name[], size_t l) { - static int can_do = -1; - - if (can_do == 0) - return 0; - can_do = false; /* We'll set it to true only if the whole process works */ - - /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the - * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is - * present only for euid == 0, hence let's use this as quick bypass check, to avoid calling mmap() if - * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but - * mmap() is not. */ - if (geteuid() != 0) - return log_debug_errno(SYNTHETIC_ERRNO(EPERM), - "Skipping PR_SET_MM, as we don't have privileges."); - - static size_t mm_size = 0; - static char *mm = NULL; +int container_get_leader(const char *machine, pid_t *pid) { + _cleanup_free_ char *s = NULL, *class = NULL; + const char *p; + pid_t leader; int r; - if (mm_size < l+1) { - size_t nn_size; - char *nn; - - nn_size = PAGE_ALIGN(l+1); - nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (nn == MAP_FAILED) - return log_debug_errno(errno, "mmap() failed: %m"); - - strncpy(nn, name, nn_size); - - /* Now, let's tell the kernel about this new memory */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - if (ERRNO_IS_PRIVILEGE(errno)) - return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m"); - - /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be - * below the desired start address, in which case the kernel may have kicked this back due - * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in - * action). The proper solution would be to have a prctl() API that could set both start+end - * simultaneously, or at least let us query the existing address to anticipate this condition - * and respond accordingly. For now, we can only guess at the cause of this failure and try - * a workaround--which will briefly expand the arg space to something potentially huge before - * resizing it to what we want. */ - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); - - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { - r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - return r; - } - - if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) - return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); - } else { - /* And update the end pointer to the new end, too. If this fails, we don't really know what - * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, - * and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); - } + assert(machine); + assert(pid); - if (mm) - (void) munmap(mm, mm_size); - - mm = nn; - mm_size = nn_size; - } else { - strncpy(mm, name, mm_size); - - /* Update the end pointer, continuing regardless of any failure. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) mm + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + if (streq(machine, ".host")) { + *pid = 1; + return 0; } - can_do = true; - return 0; -} - -int rename_process(const char name[]) { - bool truncated = false; - - /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's - * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in - * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded; - * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be - * truncated. - * - * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */ - - if (isempty(name)) - return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ - - if (!is_main_thread()) - return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we - * cache things without locking, and we make assumptions that PR_SET_NAME sets the - * process name that isn't correct on any other threads */ - - size_t l = strlen(name); - - /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we - * can use PR_SET_NAME, which sets the thread name for the calling thread. */ - if (prctl(PR_SET_NAME, name) < 0) - log_debug_errno(errno, "PR_SET_NAME failed: %m"); - if (l >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */ - truncated = true; - - /* Second step, change glibc's ID of the process name. */ - if (program_invocation_name) { - size_t k; - - k = strlen(program_invocation_name); - strncpy(program_invocation_name, name, k); - if (l > k) - truncated = true; - } + if (!hostname_is_valid(machine, 0)) + return -EINVAL; - /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but - * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline. */ - (void) update_argv(name, l); - - /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if - * it still looks here */ - if (saved_argc > 0) { - if (saved_argv[0]) { - size_t k; - - k = strlen(saved_argv[0]); - strncpy(saved_argv[0], name, k); - if (l > k) - truncated = true; - } + p = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(NULL, p, + "LEADER", &s, + "CLASS", &class); + if (r == -ENOENT) + return -EHOSTDOWN; + if (r < 0) + return r; + if (!s) + return -EIO; - for (int i = 1; i < saved_argc; i++) { - if (!saved_argv[i]) - break; + if (!streq_ptr(class, "container")) + return -EIO; - memzero(saved_argv[i], strlen(saved_argv[i])); - } - } + r = parse_pid(s, &leader); + if (r < 0) + return r; + if (leader <= 1) + return -EIO; - return !truncated; + *pid = leader; + return 0; } int is_kernel_thread(pid_t pid) { @@ -864,6 +765,23 @@ void sigterm_wait(pid_t pid) { (void) wait_for_terminate(pid, NULL); } +void sigkill_nowait(pid_t pid) { + assert(pid > 1); + + (void) kill(pid, SIGKILL); +} + +void sigkill_nowaitp(pid_t *pid) { + PROTECT_ERRNO; + + if (!pid) + return; + if (*pid <= 1) + return; + + sigkill_nowait(*pid); +} + int kill_and_sigcont(pid_t pid, int sig) { int r; @@ -1352,15 +1270,26 @@ int safe_fork_full( } if (FLAGS_SET(flags, FORK_NEW_MOUNTNS | FORK_MOUNTNS_SLAVE)) { - /* Optionally, make sure we never propagate mounts to the host. */ - if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { log_full_errno(prio, errno, "Failed to remount root directory as MS_SLAVE: %m"); _exit(EXIT_FAILURE); } } + if (FLAGS_SET(flags, FORK_PRIVATE_TMP)) { + assert(FLAGS_SET(flags, FORK_NEW_MOUNTNS)); + + /* Optionally, overmount new tmpfs instance on /tmp/. */ + r = mount_nofollow("tmpfs", "/tmp", "tmpfs", + MS_NOSUID|MS_NODEV, + "mode=01777" TMPFS_LIMITS_RUN); + if (r < 0) { + log_full_errno(prio, r, "Failed to overmount /tmp/: %m"); + _exit(EXIT_FAILURE); + } + } + if (flags & FORK_CLOSE_ALL_FDS) { /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ log_close(); @@ -1372,6 +1301,14 @@ int safe_fork_full( } } + if (flags & FORK_CLOEXEC_OFF) { + r = fd_cloexec_many(except_fds, n_except_fds, false); + if (r < 0) { + log_full_errno(prio, r, "Failed to turn off O_CLOEXEC on file descriptors: %m"); + _exit(EXIT_FAILURE); + } + } + /* When we were asked to reopen the logs, do so again now */ if (flags & FORK_REOPEN_LOG) { log_open(); @@ -1519,6 +1456,20 @@ int pidfd_get_pid(int fd, pid_t *ret) { return parse_pid(p, ret); } +int pidfd_verify_pid(int pidfd, pid_t pid) { + pid_t current_pid; + int r; + + assert(pidfd >= 0); + assert(pid > 0); + + r = pidfd_get_pid(pidfd, ¤t_pid); + if (r < 0) + return r; + + return current_pid != pid ? -ESRCH : 0; +} + static int rlimit_to_nice(rlim_t limit) { if (limit <= 1) return PRIO_MAX-1; /* i.e. 19 */ @@ -1575,40 +1526,6 @@ 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); -} - -bool invoked_by_systemd(void) { - int r; - - /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd, - * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */ - const char *e = getenv("SYSTEMD_EXEC_PID"); - if (!e) - return false; - - if (streq(e, "*")) - /* For testing. */ - return true; - - pid_t p; - r = parse_pid(e, &p); - if (r < 0) { - /* We know that systemd sets the variable correctly. Something else must have set it. */ - log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e); - return false; - } - - return getpid_cached() == p; -} - _noreturn_ void freeze(void) { log_close(); @@ -1630,31 +1547,6 @@ _noreturn_ void freeze(void) { pause(); } -bool argv_looks_like_help(int argc, char **argv) { - char **l; - - /* Scans the command line for indications the user asks for help. This is supposed to be called by - * tools that do not implement getopt() style command line parsing because they are not primarily - * user-facing. Detects four ways of asking for help: - * - * 1. Passing zero arguments - * 2. Passing "help" as first argument - * 3. Passing --help as any argument - * 4. Passing -h as any argument - */ - - if (argc <= 1) - return true; - - if (streq_ptr(argv[1], "help")) - return true; - - l = strv_skip(argv, 1); - - return strv_contains(l, "--help") || - strv_contains(l, "-h"); -} - static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", [CLD_KILLED] = "killed", diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index f8c374a310..96da0bb292 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -50,6 +50,8 @@ int get_process_environ(pid_t pid, char **ret); int get_process_ppid(pid_t pid, pid_t *ret); int get_process_umask(pid_t pid, mode_t *ret); +int container_get_leader(const char *machine, pid_t *pid); + int wait_for_terminate(pid_t pid, siginfo_t *status); typedef enum WaitFlags { @@ -66,10 +68,11 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); void sigkill_wait(pid_t pid); void sigkill_waitp(pid_t *pid); void sigterm_wait(pid_t pid); +void sigkill_nowait(pid_t pid); +void sigkill_nowaitp(pid_t *pid); int kill_and_sigcont(pid_t pid, int sig); -int rename_process(const char name[]); int is_kernel_thread(pid_t pid); int getenv_for_pid(pid_t pid, const char *field, char **_value); @@ -146,10 +149,12 @@ typedef enum ForkFlags { FORK_WAIT = 1 << 7, /* Wait until child exited */ FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */ 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 */ - FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */ + FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */ + FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ + FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */ + FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */ + FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace */ + FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */ } ForkFlags; int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); @@ -175,23 +180,12 @@ int get_oom_score_adjust(int *ret); assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX); -/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */ -#define TAKE_PID(pid) \ - ({ \ - pid_t *_ppid_ = &(pid); \ - pid_t _pid_ = *_ppid_; \ - *_ppid_ = 0; \ - _pid_; \ - }) +/* Like TAKE_PTR() but for pid_t, resetting them to 0 */ +#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 0) int pidfd_get_pid(int fd, pid_t *ret); +int pidfd_verify_pid(int pidfd, pid_t pid); int setpriority_closest(int priority); -bool invoked_as(char *argv[], const char *token); - -bool invoked_by_systemd(void); - _noreturn_ void freeze(void); - -bool argv_looks_like_help(int argc, char **argv); diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index d8734cc7d0..28ace92f19 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -75,7 +75,7 @@ static void fallback_random_bytes(void *p, size_t n) { void random_bytes(void *p, size_t n) { static bool have_getrandom = true, have_grndinsecure = true; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; if (n == 0) return; @@ -117,7 +117,7 @@ void random_bytes(void *p, size_t n) { int crypto_random_bytes(void *p, size_t n) { static bool have_getrandom = true, seen_initialized = false; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; if (n == 0) return 0; @@ -145,7 +145,7 @@ int crypto_random_bytes(void *p, size_t n) { } if (!seen_initialized) { - _cleanup_close_ int ready_fd = -1; + _cleanup_close_ int ready_fd = -EBADF; int r; ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); @@ -187,7 +187,7 @@ size_t random_pool_size(void) { } int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { - _cleanup_close_ int opened_fd = -1; + _cleanup_close_ int opened_fd = -EBADF; int r; assert(seed || size == 0); diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h index 2d99807272..b1a4d10971 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.h +++ b/src/libnm-systemd-shared/src/basic/random-util.h @@ -23,6 +23,7 @@ static inline uint32_t random_u32(void) { /* Some limits on the pool sizes when we deal with the kernel random pool */ #define RANDOM_POOL_SIZE_MIN 32U #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) +#define RANDOM_EFI_SEED_SIZE 32U size_t random_pool_size(void); diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.c b/src/libnm-systemd-shared/src/basic/ratelimit.c index c16c8f7103..f90a63b1a9 100644 --- a/src/libnm-systemd-shared/src/basic/ratelimit.c +++ b/src/libnm-systemd-shared/src/basic/ratelimit.c @@ -10,6 +10,7 @@ bool ratelimit_below(RateLimit *r) { usec_t ts; + bool good = false; assert(r); @@ -24,18 +25,12 @@ bool ratelimit_below(RateLimit *r) { /* Reset counter */ r->num = 0; - goto good; - } + good = true; + } else if (r->num < r->burst) + good = true; - if (r->num < r->burst) - goto good; - - r->num++; - return false; - -good: r->num++; - return true; + return good; } unsigned ratelimit_num_dropped(RateLimit *r) { diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c index b61c18b2de..7875ca69bb 100644 --- a/src/libnm-systemd-shared/src/basic/signal-util.c +++ b/src/libnm-systemd-shared/src/basic/signal-util.c @@ -5,6 +5,7 @@ #include "errno-util.h" #include "macro.h" +#include "missing_syscall.h" #include "parse-util.h" #include "signal-util.h" #include "stdio-util.h" @@ -282,3 +283,20 @@ int pop_pending_signal_internal(int sig, ...) { return r; /* Returns the signal popped */ } + +void propagate_signal(int sig, siginfo_t *siginfo) { + pid_t p; + + /* To be called from a signal handler. Will raise the same signal again, in our process + in our threads. + * + * Note that we use raw_getpid() instead of getpid_cached(). We might have forked with raw_clone() + * earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not + * performance sensitive code. + * + * Note that we use kill() rather than raise() as fallback, for similar reasons. */ + + p = raw_getpid(); + + if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0) + assert_se(kill(p, sig) >= 0); +} diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h index 36372c19bd..ad2ba841c6 100644 --- a/src/libnm-systemd-shared/src/basic/signal-util.h +++ b/src/libnm-systemd-shared/src/basic/signal-util.h @@ -65,3 +65,5 @@ int signal_is_blocked(int sig); int pop_pending_signal_internal(int sig, ...); #define pop_pending_signal(...) pop_pending_signal_internal(__VA_ARGS__, -1) + +void propagate_signal(int sig, siginfo_t *siginfo); diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c index f39be19a59..d7946a3641 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.c +++ b/src/libnm-systemd-shared/src/basic/socket-util.c @@ -1049,7 +1049,7 @@ ssize_t receive_one_fd_iov( if (found) *ret_fd = *(int*) CMSG_DATA(found); else - *ret_fd = -1; + *ret_fd = -EBADF; return k; } @@ -1426,7 +1426,7 @@ int socket_get_mtu(int fd, int af, size_t *ret) { } int connect_unix_path(int fd, int dir_fd, const char *path) { - _cleanup_close_ int inode_fd = -1; + _cleanup_close_ int inode_fd = -EBADF; union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; @@ -1472,3 +1472,71 @@ int connect_unix_path(int fd, int dir_fd, const char *path) { return RET_NERRNO(connect(fd, &sa.sa, salen)); } + +int socket_address_parse_unix(SocketAddress *ret_address, const char *s) { + struct sockaddr_un un; + int r; + + assert(ret_address); + assert(s); + + if (!IN_SET(*s, '/', '@')) + return -EPROTO; + + r = sockaddr_un_set_path(&un, s); + if (r < 0) + return r; + + *ret_address = (SocketAddress) { + .sockaddr.un = un, + .size = r, + }; + + return 0; +} + +int socket_address_parse_vsock(SocketAddress *ret_address, const char *s) { + /* AF_VSOCK socket in vsock:cid:port notation */ + _cleanup_free_ char *n = NULL; + char *e, *cid_start; + unsigned port, cid; + int r; + + assert(ret_address); + assert(s); + + cid_start = startswith(s, "vsock:"); + if (!cid_start) + return -EPROTO; + + e = strchr(cid_start, ':'); + if (!e) + return -EINVAL; + + r = safe_atou(e+1, &port); + if (r < 0) + return r; + + n = strndup(cid_start, e - cid_start); + if (!n) + return -ENOMEM; + + if (isempty(n)) + cid = VMADDR_CID_ANY; + else { + r = safe_atou(n, &cid); + if (r < 0) + return r; + } + + *ret_address = (SocketAddress) { + .sockaddr.vm = { + .svm_cid = cid, + .svm_family = AF_VSOCK, + .svm_port = port, + }, + .size = sizeof(struct sockaddr_vm), + }; + + return 0; +} diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index 2e36e1a56b..5cb35f65fb 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -175,15 +175,17 @@ int flush_accept(int fd); #define CMSG_FOREACH(cmsg, mh) \ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) +#define CMSG_TYPED_DATA(cmsg, type) \ + ({ \ + struct cmsghdr *_cmsg = cmsg; \ + _cmsg ? CAST_ALIGN_PTR(type, CMSG_DATA(_cmsg)) : (type*) NULL; \ + }) + struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); /* Type-safe, dereferencing version of cmsg_find() */ -#define CMSG_FIND_DATA(mh, level, type, ctype) \ - ({ \ - struct cmsghdr *_found; \ - _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \ - (ctype*) (_found ? CMSG_DATA(_found) : NULL); \ - }) +#define CMSG_FIND_DATA(mh, level, type, ctype) \ + CMSG_TYPED_DATA(cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))), ctype) /* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr" @@ -334,3 +336,9 @@ int socket_get_mtu(int fd, int af, size_t *ret); #define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID } int connect_unix_path(int fd, int dir_fd, const char *path); + +/* Parses AF_UNIX and AF_VSOCK addresses. AF_INET[6] require some netlink calls, so it cannot be in + * src/basic/ and is done from 'socket_local_address from src/shared/. Return -EPROTO in case of + * protocol mismatch. */ +int socket_address_parse_unix(SocketAddress *ret_address, const char *s); +int socket_address_parse_vsock(SocketAddress *ret_address, const char *s); diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 51adaca9d0..700e28f2c2 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -15,6 +15,7 @@ #include "fileio.h" #include "filesystems.h" #include "fs-util.h" +#include "hash-funcs.h" #include "macro.h" #include "missing_fs.h" #include "missing_magic.h" @@ -64,7 +65,7 @@ int is_device_node(const char *path) { } int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; struct dirent *buf; size_t m; @@ -162,26 +163,37 @@ int null_or_empty_fd(int fd) { return null_or_empty(&st); } -int path_is_read_only_fs(const char *path) { +static int fd_is_read_only_fs(int fd) { struct statvfs st; - assert(path); + assert(fd >= 0); - if (statvfs(path, &st) < 0) + if (fstatvfs(fd, &st) < 0) return -errno; if (st.f_flag & ST_RDONLY) return true; - /* On NFS, statvfs() might not reflect whether we can actually - * write to the remote share. Let's try again with - * access(W_OK) which is more reliable, at least sometimes. */ - if (access(path, W_OK) < 0 && errno == EROFS) + /* On NFS, fstatvfs() might not reflect whether we can actually write to the remote share. Let's try + * again with access(W_OK) which is more reliable, at least sometimes. */ + if (access_fd(fd, W_OK) == -EROFS) return true; return false; } +int path_is_read_only_fs(const char *path) { + _cleanup_close_ int fd = -EBADF; + + assert(path); + + fd = open(path, O_CLOEXEC | O_PATH); + if (fd < 0) + return -errno; + + return fd_is_read_only_fs(fd); +} + int files_same(const char *filea, const char *fileb, int flags) { struct stat a, b; @@ -441,3 +453,20 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s return 0; } + +void inode_hash_func(const struct stat *q, struct siphash *state) { + siphash24_compress(&q->st_dev, sizeof(q->st_dev), state); + siphash24_compress(&q->st_ino, sizeof(q->st_ino), state); +} + +int inode_compare_func(const struct stat *a, const struct stat *b) { + int r; + + r = CMP(a->st_dev, b->st_dev); + if (r != 0) + return r; + + return CMP(a->st_ino, b->st_ino); +} + +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free); diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h index f9519d8cbd..de11c0cf7c 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.h +++ b/src/libnm-systemd-shared/src/basic/stat-util.h @@ -11,6 +11,7 @@ #include "macro.h" #include "missing_stat.h" +#include "siphash24.h" int is_symlink(const char *path); int is_dir_full(int atfd, const char *fname, bool follow); @@ -96,3 +97,7 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s struct new_statx nsx; \ } var #endif + +void inode_hash_func(const struct stat *q, struct siphash *state); +int inode_compare_func(const struct stat *a, const struct stat *b); +extern const struct hash_ops inode_hash_ops; diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h index f647f125ac..4e93ac90c9 100644 --- a/src/libnm-systemd-shared/src/basic/stdio-util.h +++ b/src/libnm-systemd-shared/src/basic/stdio-util.h @@ -7,15 +7,20 @@ #include <sys/types.h> #include "macro.h" -#include "memory-util.h" -#define snprintf_ok(buf, len, fmt, ...) \ - ({ \ - char *_buf = (buf); \ - size_t _len = (len); \ - int _snpf = snprintf(_buf, _len, (fmt), ##__VA_ARGS__); \ - _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \ - }) +_printf_(3, 4) +static inline char *snprintf_ok(char *buf, size_t len, const char *format, ...) { + va_list ap; + int r; + + va_start(ap, format); + DISABLE_WARNING_FORMAT_NONLITERAL; + r = vsnprintf(buf, len, format, ap); + REENABLE_WARNING; + va_end(ap); + + return r >= 0 && (size_t) r < len ? buf : NULL; +} #define xsprintf(buf, fmt, ...) \ assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, ##__VA_ARGS__), "xsprintf: " #buf "[] must be big enough") @@ -26,7 +31,7 @@ do { \ size_t _i, _k; \ /* See https://github.com/google/sanitizers/issues/992 */ \ if (HAS_FEATURE_MEMORY_SANITIZER) \ - zero(_argtypes); \ + memset(_argtypes, 0, sizeof(_argtypes)); \ _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \ assert(_k < ELEMENTSOF(_argtypes)); \ for (_i = 0; _i < _k; _i++) { \ diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index 17d35fe1a4..ad8c9863bd 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -18,7 +18,6 @@ #include "strv.h" #include "terminal-util.h" #include "utf8.h" -#include "util.h" char* first_word(const char *s, const char *word) { size_t sl, wl; @@ -1188,6 +1187,49 @@ char *string_replace_char(char *str, char old_char, char new_char) { return str; } +int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) { + char *b; + + assert(s || n == 0); + assert(mode >= 0); + assert(mode < _MAKE_CSTRING_MODE_MAX); + + /* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded + * NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */ + + if (n == 0) { + if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL) + return -EINVAL; + + if (!ret) + return 0; + + b = new0(char, 1); + } else { + const char *nul; + + nul = memchr(s, 0, n); + if (nul) { + if (nul < s + n - 1 || /* embedded NUL? */ + mode == MAKE_CSTRING_REFUSE_TRAILING_NUL) + return -EINVAL; + + n--; + } else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL) + return -EINVAL; + + if (!ret) + return 0; + + b = memdup_suffix0(s, n); + } + if (!b) + return -ENOMEM; + + *ret = b; + return 0; +} + size_t strspn_from_end(const char *str, const char *accept) { size_t n = 0; @@ -1202,3 +1244,19 @@ size_t strspn_from_end(const char *str, const char *accept) { return n; } + +char *strdupspn(const char *a, const char *accept) { + if (isempty(a) || isempty(accept)) + return strdup(""); + + return strndup(a, strspn(a, accept)); +} + +char *strdupcspn(const char *a, const char *reject) { + if (isempty(a)) + return strdup(""); + if (isempty(reject)) + return strdup(a); + + return strndup(a, strcspn(a, reject)); +} diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 46681ced99..e0a47a21a9 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -53,9 +53,13 @@ static inline const char* enable_disable(bool b) { return b ? "enable" : "disable"; } -static inline const char *empty_to_null(const char *p) { - return isempty(p) ? NULL : p; -} +/* This macro's return pointer will have the "const" qualifier set or unset the same way as the input + * pointer. */ +#define empty_to_null(p) \ + ({ \ + const char *_p = (p); \ + (typeof(p)) (isempty(_p) ? NULL : _p); \ + }) static inline const char *empty_to_na(const char *p) { return isempty(p) ? "n/a" : p; @@ -74,6 +78,11 @@ static inline bool empty_or_dash(const char *str) { static inline const char *empty_or_dash_to_null(const char *p) { return empty_or_dash(p) ? NULL : p; } +#define empty_or_dash_to_null(p) \ + ({ \ + const char *_p = (p); \ + (typeof(p)) (empty_or_dash(_p) ? NULL : _p); \ + }) char *first_word(const char *s, const char *word) _pure_; @@ -230,4 +239,17 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok); char *string_replace_char(char *str, char old_char, char new_char); +typedef enum MakeCStringMode { + MAKE_CSTRING_REFUSE_TRAILING_NUL, + MAKE_CSTRING_ALLOW_TRAILING_NUL, + MAKE_CSTRING_REQUIRE_TRAILING_NUL, + _MAKE_CSTRING_MODE_MAX, + _MAKE_CSTRING_MODE_INVALID = -1, +} MakeCStringMode; + +int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret); + size_t strspn_from_end(const char *str, const char *accept); + +char *strdupspn(const char *a, const char *accept); +char *strdupcspn(const char *a, const char *reject); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index eea34ca68d..2b7a61d442 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -623,121 +623,6 @@ char** strv_remove(char **l, const char *s) { return l; } -char** strv_parse_nulstr(const char *s, size_t l) { - /* l is the length of the input data, which will be split at NULs into - * elements of the resulting strv. Hence, the number of items in the resulting strv - * will be equal to one plus the number of NUL bytes in the l bytes starting at s, - * unless s[l-1] is NUL, in which case the final empty string is not stored in - * the resulting strv, and length is equal to the number of NUL bytes. - * - * Note that contrary to a normal nulstr which cannot contain empty strings, because - * the input data is terminated by any two consequent NUL bytes, this parser accepts - * empty strings in s. - */ - - size_t c = 0, i = 0; - char **v; - - assert(s || l <= 0); - - if (l <= 0) - return new0(char*, 1); - - for (const char *p = s; p < s + l; p++) - if (*p == 0) - c++; - - if (s[l-1] != 0) - c++; - - v = new0(char*, c+1); - if (!v) - return NULL; - - for (const char *p = s; p < s + l; ) { - const char *e; - - e = memchr(p, 0, s + l - p); - - v[i] = strndup(p, e ? e - p : s + l - p); - if (!v[i]) { - strv_free(v); - return NULL; - } - - i++; - - if (!e) - break; - - p = e + 1; - } - - assert(i == c); - - return v; -} - -char** strv_split_nulstr(const char *s) { - const char *i; - char **r = NULL; - - NULSTR_FOREACH(i, s) - if (strv_extend(&r, i) < 0) { - strv_free(r); - return NULL; - } - - if (!r) - return strv_new(NULL); - - return r; -} - -int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { - /* A valid nulstr with two NULs at the end will be created, but - * q will be the length without the two trailing NULs. Thus the output - * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, - * and can also be parsed by strv_parse_nulstr as long as the length - * is provided separately. - */ - - _cleanup_free_ char *m = NULL; - size_t n = 0; - - assert(ret); - assert(ret_size); - - STRV_FOREACH(i, l) { - size_t z; - - z = strlen(*i); - - if (!GREEDY_REALLOC(m, n + z + 2)) - return -ENOMEM; - - memcpy(m + n, *i, z + 1); - n += z + 1; - } - - if (!m) { - m = new0(char, 1); - if (!m) - return -ENOMEM; - n = 1; - } else - /* make sure there is a second extra NUL at the end of resulting nulstr */ - m[n] = '\0'; - - assert(n > 0); - *ret = m; - *ret_size = n - 1; - - m = NULL; - - return 0; -} - bool strv_overlap(char * const *a, char * const *b) { STRV_FOREACH(i, a) if (strv_contains(b, *i)) @@ -904,6 +789,22 @@ rollback: return -ENOMEM; } +int strv_extend_assignment(char ***l, const char *lhs, const char *rhs) { + char *j; + + assert(l); + assert(lhs); + + if (!rhs) /* value is optional, in which case we suppress the field */ + return 0; + + j = strjoin(lhs, "=", rhs); + if (!j) + return -ENOMEM; + + return strv_consume(l, j); +} + int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { bool b = false; int r; diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index d6f5ac6ba5..1f8da85fcc 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -45,7 +45,7 @@ static inline int strv_extend(char ***l, const char *value) { return strv_extend_with_size(l, NULL, value); } -int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); +int strv_extendf(char ***l, const char *format, ...) _printf_(2,3); int strv_extend_front(char ***l, const char *value); int strv_push_with_size(char ***l, size_t *n, char *value); @@ -124,20 +124,6 @@ static inline char *strv_join(char * const *l, const char *separator) { return strv_join_full(l, separator, NULL, false); } -char** strv_parse_nulstr(const char *s, size_t l); -char** strv_split_nulstr(const char *s); -int strv_make_nulstr(char * const *l, char **p, size_t *n); - -static inline int strv_from_nulstr(char ***a, const char *nulstr) { - char **t; - - t = strv_split_nulstr(nulstr); - if (!t) - return -ENOMEM; - *a = t; - return 0; -} - bool strv_overlap(char * const *a, char * const *b) _pure_; #define _STRV_FOREACH_BACKWARDS(s, l, h, i) \ @@ -255,6 +241,8 @@ char** strv_skip(char **l, size_t n); int strv_extend_n(char ***l, const char *value, size_t n); +int strv_extend_assignment(char ***l, const char *lhs, const char *rhs); + int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); #define strv_free_and_replace(a, b) \ diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index 71b2f67350..b700f364ef 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -308,54 +308,48 @@ char *format_timestamp_style( }; struct tm tm; + bool utc, us; time_t sec; size_t n; - bool utc = false, us = false; - int r; assert(buf); + assert(style >= 0); + assert(style < _TIMESTAMP_STYLE_MAX); - switch (style) { - case TIMESTAMP_PRETTY: - case TIMESTAMP_UNIX: - break; - case TIMESTAMP_US: - us = true; - break; - case TIMESTAMP_UTC: - utc = true; - break; - case TIMESTAMP_US_UTC: - us = true; - utc = true; - break; - default: - return NULL; - } - - if (l < (size_t) (3 + /* week day */ - 1 + 10 + /* space and date */ - 1 + 8 + /* space and time */ - (us ? 1 + 6 : 0) + /* "." and microsecond part */ - 1 + 1 + /* space and shortest possible zone */ - 1)) - return NULL; /* Not enough space even for the shortest form. */ if (!timestamp_is_set(t)) return NULL; /* Timestamp is unset */ if (style == TIMESTAMP_UNIX) { - r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */ - if (r < 0 || (size_t) r >= l) - return NULL; /* Doesn't fit */ + if (l < (size_t) (1 + 1 + 1)) + return NULL; /* not enough space for even the shortest of forms */ - return buf; + return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */ } + utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE); + us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC); + + if (l < (size_t) (3 + /* week day */ + 1 + 10 + /* space and date */ + style == TIMESTAMP_DATE ? 0 : + (1 + 8 + /* space and time */ + (us ? 1 + 6 : 0) + /* "." and microsecond part */ + 1 + (utc ? 3 : 1)) + /* space and shortest possible zone */ + 1)) + return NULL; /* Not enough space even for the shortest form. */ + /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); - strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); - return buf; + static const char* const xxx[_TIMESTAMP_STYLE_MAX] = { + [TIMESTAMP_PRETTY] = "--- XXXX-XX-XX XX:XX:XX", + [TIMESTAMP_US] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX", + [TIMESTAMP_UTC] = "--- XXXX-XX-XX XX:XX:XX UTC", + [TIMESTAMP_US_UTC] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC", + [TIMESTAMP_DATE] = "--- XXXX-XX-XX", + }; + + assert(l >= strlen(xxx[style]) + 1); + return strcpy(buf, xxx[style]); } sec = (time_t) (t / USEC_PER_SEC); /* Round down */ @@ -367,6 +361,14 @@ char *format_timestamp_style( assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); memcpy(buf, weekdays[tm.tm_wday], 4); + if (style == TIMESTAMP_DATE) { + /* Special format string if only date should be shown. */ + if (strftime(buf + 3, l - 3, " %Y-%m-%d", &tm) <= 0) + return NULL; /* Doesn't fit */ + + return buf; + } + /* Add the main components */ if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) return NULL; /* Doesn't fit */ @@ -1395,7 +1397,7 @@ int get_timezones(char ***ret) { int verify_timezone(const char *name, int log_level) { bool slash = false; const char *p, *t; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; char buf[4]; int r; @@ -1568,7 +1570,7 @@ int time_change_fd(void) { .it_value.tv_sec = TIME_T_MAX, }; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX)); diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index c98f95a530..c5ae0c98d4 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -35,6 +35,7 @@ typedef enum TimestampStyle { TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_UNIX, + TIMESTAMP_DATE, _TIMESTAMP_STYLE_MAX, _TIMESTAMP_STYLE_INVALID = -EINVAL, } TimestampStyle; diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 34d3016ba9..95adf9d374 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -19,29 +19,15 @@ #include "tmpfile-util.h" #include "umask-util.h" -int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { +static int fopen_temporary_internal(int dir_fd, const char *path, FILE **ret_file) { _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *t = NULL; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; int r; - if (path) { - r = tempfn_xxxxxx(path, NULL, &t); - if (r < 0) - return r; - } else { - const char *d; - - r = tmp_dir(&d); - if (r < 0) - return r; - - t = path_join(d, "XXXXXX"); - if (!t) - return -ENOMEM; - } + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); - fd = mkostemp_safe(t); + fd = openat(dir_fd, path, O_CLOEXEC|O_NOCTTY|O_RDWR|O_CREAT|O_EXCL, 0600); if (fd < 0) return -errno; @@ -50,15 +36,59 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) { r = take_fdopen_unlocked(&fd, "w", &f); if (r < 0) { - (void) unlink(t); + (void) unlinkat(dir_fd, path, 0); return r; } - if (ret_f) - *ret_f = TAKE_PTR(f); + if (ret_file) + *ret_file = TAKE_PTR(f); + + return 0; +} + +int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) { + _cleanup_free_ char *t = NULL; + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + + r = tempfn_random(path, NULL, &t); + if (r < 0) + return r; + + r = fopen_temporary_internal(dir_fd, t, ret_file); + if (r < 0) + return r; + + if (ret_path) + *ret_path = TAKE_PTR(t); + + return 0; +} + +int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path) { + _cleanup_free_ char *t = NULL; + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + + if (!path) { + r = tmp_dir(&path); + if (r < 0) + return r; + } + + r = tempfn_random_child(path, NULL, &t); + if (r < 0) + return r; + + r = fopen_temporary_internal(dir_fd, t, ret_file); + if (r < 0) + return r; - if (ret_temp_path) - *ret_temp_path = TAKE_PTR(t); + if (ret_path) + *ret_path = TAKE_PTR(t); return 0; } @@ -71,7 +101,7 @@ int mkostemp_safe(char *pattern) { } int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; FILE *f; fd = mkostemp_safe(pattern); @@ -279,7 +309,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) { _cleanup_free_ char *path = NULL; _cleanup_fclose_ FILE *f = NULL; - _cleanup_close_ int fd = -1; + _cleanup_close_ int fd = -EBADF; assert(target); assert(ret_file); @@ -358,3 +388,23 @@ int mkdtemp_malloc(const char *template, char **ret) { *ret = TAKE_PTR(p); return 0; } + +int mkdtemp_open(const char *template, int flags, char **ret) { + _cleanup_free_ char *p = NULL; + int fd, r; + + r = mkdtemp_malloc(template, &p); + if (r < 0) + return r; + + fd = RET_NERRNO(open(p, O_DIRECTORY|O_CLOEXEC|flags)); + if (fd < 0) { + (void) rmdir(p); + return fd; + } + + if (ret) + *ret = TAKE_PTR(p); + + return fd; +} diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.h b/src/libnm-systemd-shared/src/basic/tmpfile-util.h index 610cbaf87e..e5b7709e3f 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.h +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.h @@ -1,9 +1,19 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <fcntl.h> #include <stdio.h> -int fopen_temporary(const char *path, FILE **_f, char **_temp_path); +int fopen_temporary_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path); +static inline int fopen_temporary(const char *path, FILE **ret_file, char **ret_path) { + return fopen_temporary_at(AT_FDCWD, path, ret_file, ret_path); +} + +int fopen_temporary_child_at(int dir_fd, const char *path, FILE **ret_file, char **ret_path); +static inline int fopen_temporary_child(const char *path, FILE **ret_file, char **ret_path) { + return fopen_temporary_child_at(AT_FDCWD, path, ret_file, ret_path); +} + int mkostemp_safe(char *pattern); int fmkostemp_safe(char *pattern, const char *mode, FILE**_f); @@ -19,3 +29,4 @@ int link_tmpfile(int fd, const char *path, const char *target); int flink_tmpfile(FILE *f, const char *path, const char *target); int mkdtemp_malloc(const char *template, char **ret); +int mkdtemp_open(const char *template, int flags, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/umask-util.h b/src/libnm-systemd-shared/src/basic/umask-util.h index 90d18f70ba..6f0e1cc2b2 100644 --- a/src/libnm-systemd-shared/src/basic/umask-util.h +++ b/src/libnm-systemd-shared/src/basic/umask-util.h @@ -15,12 +15,12 @@ static inline void umaskp(mode_t *u) { /* We make use of the fact here that the umask() concept is using only the lower 9 bits of mode_t, although * mode_t has space for the file type in the bits further up. We simply OR in the file type mask S_IFMT to - * distinguish the first and the second iteration of the RUN_WITH_UMASK() loop, so that we can run the first - * one, and exit on the second. */ + * distinguish the first and the second iteration of the WITH_UMASK() loop, so that we can run the first one, + * and exit on the second. */ assert_cc((S_IFMT & 0777) == 0); -#define RUN_WITH_UMASK(mask) \ +#define WITH_UMASK(mask) \ for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \ FLAGS_SET(_saved_umask_, S_IFMT); \ _saved_umask_ &= 0777) diff --git a/src/libnm-systemd-shared/src/basic/util.c b/src/libnm-systemd-shared/src/basic/util.c deleted file mode 100644 index d7ef382737..0000000000 --- a/src/libnm-systemd-shared/src/basic/util.c +++ /dev/null @@ -1,174 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include <errno.h> -#include <fcntl.h> -#include <sys/mman.h> - -#include "alloc-util.h" -#include "build.h" -#include "env-file.h" -#include "env-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "util.h" -#include "virt.h" - -int saved_argc = 0; -char **saved_argv = NULL; -static int saved_in_initrd = -1; - -bool kexec_loaded(void) { - _cleanup_free_ char *s = NULL; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0) - return false; - - return s[0] == '1'; -} - -int prot_from_flags(int flags) { - - switch (flags & O_ACCMODE) { - - case O_RDONLY: - return PROT_READ; - - case O_WRONLY: - return PROT_WRITE; - - case O_RDWR: - return PROT_READ|PROT_WRITE; - - default: - return -EINVAL; - } -} - -bool in_initrd(void) { - int r; - const char *e; - bool lenient = false; - - if (saved_in_initrd >= 0) - return saved_in_initrd; - - /* 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 - * - * 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. - */ - - 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"); - - saved_in_initrd = r > 0; - } - - 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; -} - -void in_initrd_force(bool value) { - saved_in_initrd = value; -} - -int container_get_leader(const char *machine, pid_t *pid) { - _cleanup_free_ char *s = NULL, *class = NULL; - const char *p; - pid_t leader; - int r; - - assert(machine); - assert(pid); - - if (streq(machine, ".host")) { - *pid = 1; - return 0; - } - - if (!hostname_is_valid(machine, 0)) - return -EINVAL; - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(NULL, p, - "LEADER", &s, - "CLASS", &class); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - - r = parse_pid(s, &leader); - if (r < 0) - return r; - if (leader <= 1) - return -EIO; - - *pid = leader; - return 0; -} - -int version(void) { - printf("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n%s\n", - systemd_features); - return 0; -} - -/* Turn off core dumps but only if we're running outside of a container. */ -void disable_coredumps(void) { - int r; - - if (detect_container() > 0) - return; - - r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", WRITE_STRING_FILE_DISABLE_BUFFER); - if (r < 0) - log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); -} diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index 2536c741c6..c4ad957588 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#ifndef SD_BOOT +#if !SD_BOOT # include <assert.h> #endif @@ -20,6 +20,7 @@ #define _hidden_ __attribute__((__visibility__("hidden"))) #define _likely_(x) (__builtin_expect(!!(x), 1)) #define _malloc_ __attribute__((__malloc__)) +#define _noinline_ __attribute__((noinline)) #define _noreturn_ _Noreturn #define _packed_ __attribute__((__packed__)) #define _printf_(a, b) __attribute__((__format__(printf, a, b))) @@ -66,18 +67,18 @@ #define XCONCATENATE(x, y) x ## y #define CONCATENATE(x, y) XCONCATENATE(x, y) -#ifdef SD_BOOT +#if SD_BOOT _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function); #ifdef NDEBUG #define assert(expr) #define assert_not_reached() __builtin_unreachable() #else - #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) - #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); }) + #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__) #endif #define static_assert _Static_assert - #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) + #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); }) #endif /* This passes the argument through after (if asserts are enabled) checking that it is not null. */ @@ -251,47 +252,44 @@ (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 CASE_F_1(X) case X: +#define CASE_F_2(X, ...) case X: CASE_F_1( __VA_ARGS__) +#define CASE_F_3(X, ...) case X: CASE_F_2( __VA_ARGS__) +#define CASE_F_4(X, ...) case X: CASE_F_3( __VA_ARGS__) +#define CASE_F_5(X, ...) case X: CASE_F_4( __VA_ARGS__) +#define CASE_F_6(X, ...) case X: CASE_F_5( __VA_ARGS__) +#define CASE_F_7(X, ...) case X: CASE_F_6( __VA_ARGS__) +#define CASE_F_8(X, ...) case X: CASE_F_7( __VA_ARGS__) +#define CASE_F_9(X, ...) case X: CASE_F_8( __VA_ARGS__) +#define CASE_F_10(X, ...) case X: CASE_F_9( __VA_ARGS__) +#define CASE_F_11(X, ...) case X: CASE_F_10( __VA_ARGS__) +#define CASE_F_12(X, ...) case X: CASE_F_11( __VA_ARGS__) +#define CASE_F_13(X, ...) case X: CASE_F_12( __VA_ARGS__) +#define CASE_F_14(X, ...) case X: CASE_F_13( __VA_ARGS__) +#define CASE_F_15(X, ...) case X: CASE_F_14( __VA_ARGS__) +#define CASE_F_16(X, ...) case X: CASE_F_15( __VA_ARGS__) +#define CASE_F_17(X, ...) case X: CASE_F_16( __VA_ARGS__) +#define CASE_F_18(X, ...) case X: CASE_F_17( __VA_ARGS__) +#define CASE_F_19(X, ...) case X: CASE_F_18( __VA_ARGS__) +#define CASE_F_20(X, ...) case X: CASE_F_19( __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__) + (__VA_ARGS__) -#define IN_SET(x, ...) \ +#define IN_SET(x, first, ...) \ ({ \ 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__ }; \ + /* If the build breaks in the line below, you need to extend the case macros. We use typeof(+x) \ + * here to widen the type of x if it is a bit-field as this would otherwise be illegal. */ \ + static const typeof(+x) __assert_in_set[] _unused_ = { first, __VA_ARGS__ }; \ assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ switch (x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ + FOR_EACH_MAKE_CASE(first, __VA_ARGS__) \ _found = true; \ - break; \ + break; \ default: \ break; \ } \ @@ -300,13 +298,18 @@ /* 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) *_pptr_ = &(ptr); \ - typeof(ptr) _ptr_ = *_pptr_; \ - *_pptr_ = NULL; \ - _ptr_; \ +#define TAKE_GENERIC(var, type, nullvalue) \ + ({ \ + type *_pvar_ = &(var); \ + type _var_ = *_pvar_; \ + type _nullvalue_ = nullvalue; \ + *_pvar_ = _nullvalue_; \ + _var_; \ }) +#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL) +#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr)) +#define TAKE_STRUCT_TYPE(s, type) TAKE_GENERIC(s, type, {}) +#define TAKE_STRUCT(s) TAKE_STRUCT_TYPE(s, typeof(s)) /* * STRLEN - return the length of a string literal, minus the trailing NUL byte. @@ -330,14 +333,23 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { return ((l + ali - 1) & ~(ali - 1)); } +#define ALIGN2(l) ALIGN_TO(l, 2) #define ALIGN4(l) ALIGN_TO(l, 4) #define ALIGN8(l) ALIGN_TO(l, 8) -#ifndef SD_BOOT +#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p)) +#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p)) +#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p)) +#if !SD_BOOT /* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */ #define ALIGN(l) ALIGN_TO(l, sizeof(void*)) #define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p))) #endif +/* Checks if the specified pointer is aligned as appropriate for the specific type */ +#define IS_ALIGNED16(p) (((uintptr_t) p) % __alignof__(uint16_t) == 0) +#define IS_ALIGNED32(p) (((uintptr_t) p) % __alignof__(uint32_t) == 0) +#define IS_ALIGNED64(p) (((uintptr_t) p) % __alignof__(uint64_t) == 0) + /* Same as ALIGN_TO but callable in constant contexts. */ #define CONST_ALIGN_TO(l, ali) \ __builtin_choose_expr( \ @@ -348,9 +360,31 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) { ((l) + (ali) - 1) & ~((ali) - 1), \ VOID_0) +/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable + * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a + * warning or if you want to assert that the cast gives a pointer of suitable alignment. */ +#define CAST_ALIGN_PTR(t, p) \ + ({ \ + const void *_p = (p); \ + assert(((uintptr_t) _p) % __alignof__(t) == 0); \ + (t *) _p; \ + }) + #define UPDATE_FLAG(orig, flag, b) \ ((b) ? ((orig) | (flag)) : ((orig) & ~(flag))) #define SET_FLAG(v, flag, b) \ (v) = UPDATE_FLAG(v, flag, b) #define FLAGS_SET(v, flags) \ ((~(v) & (flags)) == 0) + +/* Declare a flexible array usable in a union. + * This is essentially a work-around for a pointless constraint in C99 + * and might go away in some future version of the standard. + * + * See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=3080ea5553cc909b000d1f1d964a9041962f2c5b + */ +#define DECLARE_FLEX_ARRAY(type, name) \ + struct { \ + dummy_t __empty__ ## name; \ + type name[]; \ + } diff --git a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h new file mode 100644 index 0000000000..78e2dbec59 --- /dev/null +++ b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <stddef.h> + +#if SD_BOOT +# include "efi-string.h" +#else +# include <string.h> +#endif + +#include "macro-fundamental.h" + +#if !SD_BOOT && HAVE_EXPLICIT_BZERO +static inline void *explicit_bzero_safe(void *p, size_t l) { + if (p && l > 0) + explicit_bzero(p, l); + + return p; +} +#else +static inline void *explicit_bzero_safe(void *p, size_t l) { + if (p && l > 0) { + memset(p, 0, l); + __asm__ __volatile__("" : : "r"(p) : "memory"); + } + return p; +} +#endif + +struct VarEraser { + /* NB: This is a pointer to memory to erase in case of CLEANUP_ERASE(). Pointer to pointer to memory + * to erase in case of CLEANUP_ERASE_PTR() */ + void *p; + size_t size; +}; + +static inline void erase_var(struct VarEraser *e) { + explicit_bzero_safe(e->p, e->size); +} + +/* Mark var to be erased when leaving scope. */ +#define CLEANUP_ERASE(var) \ + _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \ + .p = &(var), \ + .size = sizeof(var), \ + } + +static inline void erase_varp(struct VarEraser *e) { + + /* Very similar to erase_var(), but assumes `p` is a pointer to a pointer whose memory shall be destructed. */ + if (!e->p) + return; + + explicit_bzero_safe(*(void**) e->p, e->size); +} + +/* Mark pointer so that memory pointed to is erased when leaving scope. Note: this takes a pointer to the + * specified pointer, instead of just a copy of it. This is to allow callers to invalidate the pointer after + * use, if they like, disabling our automatic erasure (for example because they succeeded with whatever they + * wanted to do and now intend to return the allocated buffer to their caller without it being erased). */ +#define CLEANUP_ERASE_PTR(ptr, sz) \ + _cleanup_(erase_varp) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { \ + .p = (ptr), \ + .size = (sz), \ + } diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c index 9b717645b3..4389e9e37c 100644 --- a/src/libnm-systemd-shared/src/fundamental/sha256.c +++ b/src/libnm-systemd-shared/src/fundamental/sha256.c @@ -22,7 +22,7 @@ <https://www.gnu.org/licenses/>. */ #include <stdbool.h> -#ifdef SD_BOOT +#if SD_BOOT # include "efi-string.h" #else # include <string.h> @@ -30,6 +30,7 @@ #include "macro-fundamental.h" #include "sha256.h" +#include "unaligned-fundamental.h" #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define SWAP(n) \ @@ -48,14 +49,6 @@ # define SWAP64(n) (n) #endif -/* The condition below is from glibc's string/string-inline.c. - * See definition of _STRING_INLINE_unaligned. */ -#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__) -# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__(uint32_t) != 0) -#else -# define UNALIGNED_P(p) false -#endif - /* This array contains the bytes used to pad the buffer to the next 64-byte boundary. (FIPS 180-2:5.1.1) */ static const uint8_t fillbuf[64] = { @@ -128,11 +121,7 @@ uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_ /* Put result from CTX in first 32 bytes following RESBUF. */ for (size_t i = 0; i < 8; ++i) - if (UNALIGNED_P(resbuf)) - memcpy(resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t)); - else - ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]); - + unaligned_write_ne32(resbuf + i * sizeof(uint32_t), SWAP(ctx->H[i])); return resbuf; } @@ -165,18 +154,17 @@ void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx /* Process available complete blocks. */ if (len >= 64) { - if (UNALIGNED_P(buffer)) + if (IS_ALIGNED32(buffer)) { + sha256_process_block(buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } else while (len > 64) { memcpy(ctx->buffer, buffer, 64); sha256_process_block(ctx->buffer, 64, ctx); buffer = (const char *) buffer + 64; len -= 64; } - else { - sha256_process_block(buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } } /* Move remaining bytes into internal buffer. */ diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.h b/src/libnm-systemd-shared/src/fundamental/sha256.h index 31790c2ebd..dbb08e35e5 100644 --- a/src/libnm-systemd-shared/src/fundamental/sha256.h +++ b/src/libnm-systemd-shared/src/fundamental/sha256.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "stdint.h" +#include <stddef.h> +#include <stdint.h> #define SHA256_DIGEST_SIZE 32 @@ -28,6 +29,11 @@ void sha256_init_ctx(struct sha256_ctx *ctx); uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]); void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx); +static inline void sha256_process_bytes_and_size(const void *buffer, size_t len, struct sha256_ctx *ctx) { + sha256_process_bytes(&len, sizeof(len), ctx); + sha256_process_bytes(buffer, len, ctx); +} + uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]); #define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {}) diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c index 11701ebe52..484131d72a 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef SD_BOOT +#if !SD_BOOT # include <ctype.h> #endif @@ -20,7 +20,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) { return (sd_char*) s + l; } -#ifndef SD_BOOT +#if !SD_BOOT sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) { size_t l; diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h index ecf32e519f..c35ce5b88f 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#ifdef SD_BOOT +#if SD_BOOT # include <efi.h> # include <efilib.h> # include "efi-string.h" @@ -11,7 +11,7 @@ #include "macro-fundamental.h" -#ifdef SD_BOOT +#if SD_BOOT # define strlen strlen16 # define strcmp strcmp16 # define strncmp strncmp16 @@ -59,7 +59,7 @@ static inline size_t strlen_ptr(const sd_char *s) { } sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_; -#ifndef SD_BOOT +#if !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_; @@ -110,6 +110,10 @@ static inline bool ascii_isdigit(sd_char a) { return a >= '0' && a <= '9'; } +static inline bool ascii_ishex(sd_char a) { + return ascii_isdigit(a) || (a >= 'a' && a <= 'f') || (a >= 'A' && a <= 'F'); +} + static inline bool ascii_isalpha(sd_char a) { /* A pure ASCII, locale independent version of isalpha() */ return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c index ebf86d2405..620b156563 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.c +++ b/src/libnm-systemd-shared/src/shared/dns-domain.c @@ -297,7 +297,6 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) { int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { _cleanup_free_ uint32_t *input = NULL; size_t input_size, l; - const char *p; bool contains_8bit = false; char buffer[DNS_LABEL_MAX+1]; int r; @@ -314,7 +313,7 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded if (encoded_size <= 0) return -EINVAL; - for (p = encoded; p < encoded + encoded_size; p++) + for (const char *p = encoded; p < encoded + encoded_size; p++) if ((uint8_t) *p > 127) contains_8bit = true; @@ -527,7 +526,18 @@ int dns_name_compare_func(const char *a, const char *b) { } } -DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func); +DEFINE_HASH_OPS( + dns_name_hash_ops, + char, + dns_name_hash_func, + dns_name_compare_func); + +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( + dns_name_hash_ops_free, + char, + dns_name_hash_func, + dns_name_compare_func, + free); int dns_name_equal(const char *x, const char *y) { int r, q; @@ -745,9 +755,8 @@ int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_ad return r; if (r > 0) { uint8_t a[4]; - unsigned i; - for (i = 0; i < ELEMENTSOF(a); i++) { + for (size_t i = 0; i < ELEMENTSOF(a); i++) { char label[DNS_LABEL_MAX+1]; r = dns_label_unescape(&p, label, sizeof label, 0); @@ -781,9 +790,8 @@ int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_ad return r; if (r > 0) { struct in6_addr a; - unsigned i; - for (i = 0; i < ELEMENTSOF(a.s6_addr); i++) { + for (size_t i = 0; i < ELEMENTSOF(a.s6_addr); i++) { char label[DNS_LABEL_MAX+1]; int x, y; @@ -824,7 +832,6 @@ int dns_name_address(const char *p, int *ret_family, union in_addr_union *ret_ad } bool dns_name_is_root(const char *name) { - assert(name); /* There are exactly two ways to encode the root domain name: @@ -895,8 +902,6 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo } static bool srv_type_label_is_valid(const char *label, size_t n) { - size_t k; - assert(label); if (n < 2) /* Label needs to be at least 2 chars long */ @@ -910,12 +915,11 @@ static bool srv_type_label_is_valid(const char *label, size_t n) { return false; /* Third and further chars must be alphanumeric or a hyphen */ - for (k = 2; k < n; k++) { + for (size_t k = 2; k < n; k++) if (!ascii_isalpha(label[k]) && !ascii_isdigit(label[k]) && label[k] != '-') return false; - } return true; } @@ -1111,14 +1115,12 @@ finish: } static int dns_name_build_suffix_table(const char *name, const char *table[]) { - const char *p; + const char *p = ASSERT_PTR(name); unsigned n = 0; int r; - assert(name); assert(table); - p = name; for (;;) { if (n > DNS_N_LABELS_MAX) return -EINVAL; diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h index 5421c60ee7..331fb89637 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.h +++ b/src/libnm-systemd-shared/src/shared/dns-domain.h @@ -60,13 +60,10 @@ static inline int dns_name_is_valid_ldh(const char *s) { return 1; } -static inline bool dns_name_is_empty(const char *s) { - return isempty(s) || streq(s, "."); -} - void dns_name_hash_func(const char *s, struct siphash *state); int dns_name_compare_func(const char *a, const char *b); extern const struct hash_ops dns_name_hash_ops; +extern const struct hash_ops dns_name_hash_ops_free; int dns_name_between(const char *a, const char *b, const char *c); int dns_name_equal(const char *x, const char *y); |