systemd: merge branch systemd into main

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1068
This commit is contained in:
Thomas Haller 2022-01-24 20:34:37 +01:00
commit 18cb8205b3
No known key found for this signature in database
GPG Key ID: 29C2366E4DFC5728
92 changed files with 2750 additions and 1350 deletions

View File

@ -2122,8 +2122,10 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/dhcp-server-internal.h \
src/libnm-systemd-shared/sd-adapt-shared/dirent-util.h \
src/libnm-systemd-shared/sd-adapt-shared/errno-list.h \
src/libnm-systemd-shared/sd-adapt-shared/filesystems.h \
src/libnm-systemd-shared/sd-adapt-shared/glob-util.h \
src/libnm-systemd-shared/sd-adapt-shared/gunicode.h \
src/libnm-systemd-shared/sd-adapt-shared/hmac.h \
src/libnm-systemd-shared/sd-adapt-shared/idn-util.h \
src/libnm-systemd-shared/sd-adapt-shared/ioprio.h \
src/libnm-systemd-shared/sd-adapt-shared/locale-util.h \
@ -2138,6 +2140,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/missing_timerfd.h \
src/libnm-systemd-shared/sd-adapt-shared/mkdir.h \
src/libnm-systemd-shared/sd-adapt-shared/namespace-util.h \
src/libnm-systemd-shared/sd-adapt-shared/netif-util.h \
src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h \
src/libnm-systemd-shared/sd-adapt-shared/nulstr-util.h \
src/libnm-systemd-shared/sd-adapt-shared/os-util.h \
@ -2245,7 +2248,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/fundamental/macro-fundamental.h \
src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c \
src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h \
src/libnm-systemd-shared/src/fundamental/type.h \
src/libnm-systemd-shared/src/fundamental/types-fundamental.h \
src/libnm-systemd-shared/src/shared/dns-domain.c \
src/libnm-systemd-shared/src/shared/dns-domain.h \
src/libnm-systemd-shared/src/shared/log-link.h \

View File

@ -16,7 +16,7 @@
#include "unaligned.h"
#include "util.h"
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) {
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 ? */
@ -38,30 +38,21 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
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_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
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_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
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_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
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_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
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_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
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_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* X xor A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_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 = {
@ -77,7 +68,7 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
return 0;
}
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) {
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac) {
union sockaddr_union link = {
.ll.sll_family = AF_PACKET,
.ll.sll_protocol = htobe16(ETH_P_ARP),
@ -89,12 +80,13 @@ int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const stru
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, eth_mac);
r = arp_update_filter(s, a, mac);
if (r < 0)
return r;

View File

@ -11,8 +11,8 @@
#include "socket-util.h"
#include "sparse-endian.h"
int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac);
int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac);
int arp_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,

View File

@ -11,7 +11,7 @@
#include "dhcp-identifier.h"
#include "dhcp6-protocol.h"
#include "network-util.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "stat-util.h"
@ -200,7 +200,7 @@ int dhcp_identifier_set_iaid(
/* device is under renaming */
return -EBUSY;
name = net_get_name_persistent(device);
name = net_get_persistent_name(device);
}
if (name)
@ -214,7 +214,7 @@ int dhcp_identifier_set_iaid(
if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */
id32 = __bswap_32(id32);
id32 = bswap_32(id32);
else
/* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy

View File

@ -53,8 +53,8 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen,
size_t *optoffset);
uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
size_t optlen, size_t *optoffset);
uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);

View File

@ -70,6 +70,12 @@ struct sd_dhcp_lease {
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);
};

View File

@ -60,24 +60,20 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
/* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
* compare chaddr for ETH_ALEN bytes. */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 12), /* A (the MAC address length) == ETH_ALEN ? */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */
BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */
BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */
BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
};
struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
@ -117,10 +113,17 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
return TAKE_FD(s);
}
int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
const uint8_t *mac_addr, size_t mac_addr_len,
const uint8_t *bcast_addr, size_t bcast_addr_len,
uint16_t arp_type, uint16_t port) {
int dhcp_network_bind_raw_socket(
int ifindex,
union sockaddr_union *link,
uint32_t xid,
const uint8_t *mac_addr,
size_t mac_addr_len,
const uint8_t *bcast_addr,
size_t bcast_addr_len,
uint16_t arp_type,
uint16_t port) {
static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* Default broadcast address for IPoIB */
static const uint8_t ib_bcast[] = {
@ -174,7 +177,6 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
else
r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
if (r < 0)
return r;
@ -206,43 +208,49 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
return r;
}
r = bind(s, &src.sa, sizeof(src.in));
if (r < 0)
if (bind(s, &src.sa, sizeof(src.in)) < 0)
return -errno;
return TAKE_FD(s);
}
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
const void *packet, size_t len) {
int r;
int dhcp_network_send_raw_socket(
int s,
const union sockaddr_union *link,
const void *packet,
size_t len) {
/* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this
* function should fail with negative errno. */
assert(link);
assert(packet);
assert(len);
assert(len > 0);
r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll));
if (r < 0)
if (sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll)) < 0)
return -errno;
return 0;
}
int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
const void *packet, size_t len) {
int dhcp_network_send_udp_socket(
int s,
be32_t address,
uint16_t port,
const void *packet,
size_t len) {
union sockaddr_union dest = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(port),
.in.sin_addr.s_addr = address,
};
int r;
assert(s >= 0);
assert(packet);
assert(len);
assert(len > 0);
r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
if (r < 0)
if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
return -errno;
return 0;

View File

@ -12,21 +12,44 @@
#include "dhcp-internal.h"
#include "dhcp-protocol.h"
#include "memory-util.h"
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
uint8_t type, uint16_t arp_type, size_t optlen,
size_t *optoffset) {
int dhcp_message_init(
DHCPMessage *message,
uint8_t op,
uint32_t xid,
uint8_t type,
uint16_t arp_type,
uint8_t hlen,
const uint8_t *chaddr,
size_t optlen,
size_t *optoffset) {
size_t offset = 0;
int r;
assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
assert(chaddr || hlen == 0);
message->op = op;
message->htype = arp_type;
message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
/* RFC2131 section 4.1.1:
The client MUST include its hardware address in the chaddr field, if
necessary for delivery of DHCP reply messages.
RFC 4390 section 2.1:
A DHCP client, when working over an IPoIB interface, MUST follow the
following rules:
"htype" (hardware address type) MUST be 32 [ARPPARAM].
"hlen" (hardware address length) MUST be 0.
"chaddr" (client hardware address) field MUST be zeroed.
*/
message->hlen = (arp_type == ARPHRD_INFINIBAND) ? 0 : hlen;
memcpy_safe(message->chaddr, chaddr, message->hlen);
message->xid = htobe32(xid);
message->magic = htobe32(DHCP_MAGIC_COOKIE);

View File

@ -11,6 +11,7 @@
#include "sd-event.h"
#include "sd-dhcp6-client.h"
#include "dhcp6-protocol.h"
#include "hashmap.h"
#include "list.h"
#include "macro.h"
@ -93,6 +94,7 @@ typedef struct DHCP6IA {
typedef struct sd_dhcp6_client sd_dhcp6_client;
bool dhcp6_option_can_request(uint16_t option);
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
@ -129,6 +131,12 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
int client_parse_message(
sd_dhcp6_client *client,
DHCP6Message *message,
size_t len,
sd_dhcp6_lease *lease);
const char *dhcp6_message_type_to_string(int s) _const_;
int dhcp6_message_type_from_string(const char *s) _pure_;
const char *dhcp6_message_status_to_string(int s) _const_;

View File

@ -14,10 +14,14 @@
struct sd_dhcp6_lease {
unsigned n_ref;
uint8_t *clientid;
size_t clientid_len;
uint8_t *serverid;
size_t serverid_len;
uint8_t preference;
bool rapid_commit;
triple_timestamp timestamp;
struct in6_addr server_address;
DHCP6IA ia;
DHCP6IA pd;
@ -39,21 +43,19 @@ struct sd_dhcp6_lease {
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
size_t len);
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len);
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ;
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret);

View File

@ -49,6 +49,10 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
if (r < 0)
return r;
r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
if (r < 0)
return r;
r = bind(s, &src.sa, sizeof(src.in6));
if (r < 0)
return -errno;

View File

@ -26,6 +26,193 @@
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
bool dhcp6_option_can_request(uint16_t option) {
/* See Client ORO field in
* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
switch (option) {
case SD_DHCP6_OPTION_CLIENTID:
case SD_DHCP6_OPTION_SERVERID:
case SD_DHCP6_OPTION_IA_NA:
case SD_DHCP6_OPTION_IA_TA:
case SD_DHCP6_OPTION_IAADDR:
case SD_DHCP6_OPTION_ORO:
case SD_DHCP6_OPTION_PREFERENCE:
case SD_DHCP6_OPTION_ELAPSED_TIME:
case SD_DHCP6_OPTION_RELAY_MSG:
case SD_DHCP6_OPTION_AUTH:
case SD_DHCP6_OPTION_UNICAST:
case SD_DHCP6_OPTION_STATUS_CODE:
case SD_DHCP6_OPTION_RAPID_COMMIT:
case SD_DHCP6_OPTION_USER_CLASS:
case SD_DHCP6_OPTION_VENDOR_CLASS:
return false;
case SD_DHCP6_OPTION_VENDOR_OPTS:
return true;
case SD_DHCP6_OPTION_INTERFACE_ID:
case SD_DHCP6_OPTION_RECONF_MSG:
case SD_DHCP6_OPTION_RECONF_ACCEPT:
return false;
case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME:
case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS:
case SD_DHCP6_OPTION_DNS_SERVERS:
case SD_DHCP6_OPTION_DOMAIN_LIST:
return true;
case SD_DHCP6_OPTION_IA_PD:
case SD_DHCP6_OPTION_IA_PD_PREFIX:
return false;
case SD_DHCP6_OPTION_NIS_SERVERS:
case SD_DHCP6_OPTION_NISP_SERVERS:
case SD_DHCP6_OPTION_NIS_DOMAIN_NAME:
case SD_DHCP6_OPTION_NISP_DOMAIN_NAME:
case SD_DHCP6_OPTION_SNTP_SERVERS:
case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
case SD_DHCP6_OPTION_BCMCS_SERVER_D:
case SD_DHCP6_OPTION_BCMCS_SERVER_A:
case SD_DHCP6_OPTION_GEOCONF_CIVIC:
return true;
case SD_DHCP6_OPTION_REMOTE_ID:
case SD_DHCP6_OPTION_SUBSCRIBER_ID:
return false;
case SD_DHCP6_OPTION_CLIENT_FQDN:
case SD_DHCP6_OPTION_PANA_AGENT:
case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE:
case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE:
return true;
case SD_DHCP6_OPTION_ERO:
case SD_DHCP6_OPTION_LQ_QUERY:
case SD_DHCP6_OPTION_CLIENT_DATA:
case SD_DHCP6_OPTION_CLT_TIME:
case SD_DHCP6_OPTION_LQ_RELAY_DATA:
case SD_DHCP6_OPTION_LQ_CLIENT_LINK:
return false;
case SD_DHCP6_OPTION_MIP6_HNIDF:
case SD_DHCP6_OPTION_MIP6_VDINF:
case SD_DHCP6_OPTION_V6_LOST:
case SD_DHCP6_OPTION_CAPWAP_AC_V6:
return true;
case SD_DHCP6_OPTION_RELAY_ID:
return false;
case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS:
case SD_DHCP6_OPTION_IPV6_FQDN_MOS:
case SD_DHCP6_OPTION_NTP_SERVER:
case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN:
case SD_DHCP6_OPTION_SIP_UA_CS_LIST:
case SD_DHCP6_OPTION_BOOTFILE_URL:
case SD_DHCP6_OPTION_BOOTFILE_PARAM:
return true;
case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE:
return false;
case SD_DHCP6_OPTION_NII:
case SD_DHCP6_OPTION_GEOLOCATION:
case SD_DHCP6_OPTION_AFTR_NAME:
case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME:
return true;
case SD_DHCP6_OPTION_RSOO:
return false;
case SD_DHCP6_OPTION_PD_EXCLUDE:
return true;
case SD_DHCP6_OPTION_VSS:
return false;
case SD_DHCP6_OPTION_MIP6_IDINF:
case SD_DHCP6_OPTION_MIP6_UDINF:
case SD_DHCP6_OPTION_MIP6_HNP:
case SD_DHCP6_OPTION_MIP6_HAA:
case SD_DHCP6_OPTION_MIP6_HAF:
case SD_DHCP6_OPTION_RDNSS_SELECTION:
case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME:
case SD_DHCP6_OPTION_KRB_REALM_NAME:
case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME:
case SD_DHCP6_OPTION_KRB_KDC:
return true;
case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR:
case SD_DHCP6_OPTION_LINK_ADDRESS:
case SD_DHCP6_OPTION_RADIUS:
return false;
case SD_DHCP6_OPTION_SOL_MAX_RT:
case SD_DHCP6_OPTION_INF_MAX_RT:
case SD_DHCP6_OPTION_ADDRSEL:
case SD_DHCP6_OPTION_ADDRSEL_TABLE:
case SD_DHCP6_OPTION_V6_PCP_SERVER:
return true;
case SD_DHCP6_OPTION_DHCPV4_MSG:
return false;
case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER:
return true;
case SD_DHCP6_OPTION_S46_RULE:
return false;
case SD_DHCP6_OPTION_S46_BR:
return true;
case SD_DHCP6_OPTION_S46_DMR:
case SD_DHCP6_OPTION_S46_V4V6BIND:
case SD_DHCP6_OPTION_S46_PORTPARAMS:
return false;
case SD_DHCP6_OPTION_S46_CONT_MAPE:
case SD_DHCP6_OPTION_S46_CONT_MAPT:
case SD_DHCP6_OPTION_S46_CONT_LW:
case SD_DHCP6_OPTION_4RD:
case SD_DHCP6_OPTION_4RD_MAP_RULE:
case SD_DHCP6_OPTION_4RD_NON_MAP_RULE:
return true;
case SD_DHCP6_OPTION_LQ_BASE_TIME:
case SD_DHCP6_OPTION_LQ_START_TIME:
case SD_DHCP6_OPTION_LQ_END_TIME:
return false;
case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
case SD_DHCP6_OPTION_MPL_PARAMETERS:
return true;
case SD_DHCP6_OPTION_ANI_ATT:
case SD_DHCP6_OPTION_ANI_NETWORK_NAME:
case SD_DHCP6_OPTION_ANI_AP_NAME:
case SD_DHCP6_OPTION_ANI_AP_BSSID:
case SD_DHCP6_OPTION_ANI_OPERATOR_ID:
case SD_DHCP6_OPTION_ANI_OPERATOR_REALM:
return false;
case SD_DHCP6_OPTION_S46_PRIORITY:
return true;
case SD_DHCP6_OPTION_MUD_URL_V6:
return false;
case SD_DHCP6_OPTION_V6_PREFIX64:
return true;
case SD_DHCP6_OPTION_F_BINDING_STATUS:
case SD_DHCP6_OPTION_F_CONNECT_FLAGS:
case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO:
case SD_DHCP6_OPTION_F_DNS_HOST_NAME:
case SD_DHCP6_OPTION_F_DNS_ZONE_NAME:
case SD_DHCP6_OPTION_F_DNS_FLAGS:
case SD_DHCP6_OPTION_F_EXPIRATION_TIME:
case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD:
case SD_DHCP6_OPTION_F_MCLT:
case SD_DHCP6_OPTION_F_PARTNER_LIFETIME:
case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT:
case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME:
case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME:
case SD_DHCP6_OPTION_F_PROTOCOL_VERSION:
case SD_DHCP6_OPTION_F_KEEPALIVE_TIME:
case SD_DHCP6_OPTION_F_RECONFIGURE_DATA:
case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME:
case SD_DHCP6_OPTION_F_SERVER_FLAGS:
case SD_DHCP6_OPTION_F_SERVER_STATE:
case SD_DHCP6_OPTION_F_START_TIME_OF_STATE:
case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME:
case SD_DHCP6_OPTION_RELAY_PORT:
return false;
case SD_DHCP6_OPTION_V6_SZTP_REDIRECT:
case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX:
return true;
case SD_DHCP6_OPTION_IA_LL:
case SD_DHCP6_OPTION_LLADDR:
case SD_DHCP6_OPTION_SLAP_QUAD:
return false;
case SD_DHCP6_OPTION_V6_DOTS_RI:
case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
return true;
default:
return false;
}
}
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
DHCP6Option *option;
@ -275,7 +462,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
if (dns_name_is_single_label(fqdn))
r--;
r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
return r;
}

View File

@ -5,6 +5,7 @@
Copyright © 2014 Intel Corporation. All rights reserved.
***/
#include <errno.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
@ -36,57 +37,83 @@ enum {
DHCP6_PORT_CLIENT = 546,
};
#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC
#define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC
#define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC
#define DHCP6_REQ_TIMEOUT 1 * USEC_PER_SEC
#define DHCP6_REQ_MAX_RT 120 * USEC_PER_SEC
#define DHCP6_INF_TIMEOUT (1 * USEC_PER_SEC)
#define DHCP6_INF_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_SOL_MAX_DELAY (1 * USEC_PER_SEC)
#define DHCP6_SOL_TIMEOUT (1 * USEC_PER_SEC)
#define DHCP6_SOL_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_REQ_TIMEOUT (1 * USEC_PER_SEC)
#define DHCP6_REQ_MAX_RT (120 * USEC_PER_SEC)
#define DHCP6_REQ_MAX_RC 10
#define DHCP6_REN_TIMEOUT 10 * USEC_PER_SEC
#define DHCP6_REN_MAX_RT 600 * USEC_PER_SEC
#define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC
#define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC
#define DHCP6_REN_TIMEOUT (10 * USEC_PER_SEC)
#define DHCP6_REN_MAX_RT (600 * USEC_PER_SEC)
#define DHCP6_REB_TIMEOUT (10 * USEC_PER_SEC)
#define DHCP6_REB_MAX_RT (600 * USEC_PER_SEC)
enum DHCP6State {
DHCP6_STATE_STOPPED = 0,
DHCP6_STATE_INFORMATION_REQUEST = 1,
DHCP6_STATE_SOLICITATION = 2,
DHCP6_STATE_REQUEST = 3,
DHCP6_STATE_BOUND = 4,
DHCP6_STATE_RENEW = 5,
DHCP6_STATE_REBIND = 6,
};
typedef enum DHCP6State {
DHCP6_STATE_STOPPED,
DHCP6_STATE_INFORMATION_REQUEST,
DHCP6_STATE_SOLICITATION,
DHCP6_STATE_REQUEST,
DHCP6_STATE_BOUND,
DHCP6_STATE_RENEW,
DHCP6_STATE_REBIND,
_DHCP6_STATE_MAX,
_DHCP6_STATE_INVALID = -EINVAL,
} DHCP6State;
enum {
DHCP6_SOLICIT = 1,
DHCP6_ADVERTISE = 2,
DHCP6_REQUEST = 3,
DHCP6_CONFIRM = 4,
DHCP6_RENEW = 5,
DHCP6_REBIND = 6,
DHCP6_REPLY = 7,
DHCP6_RELEASE = 8,
DHCP6_DECLINE = 9,
DHCP6_RECONFIGURE = 10,
DHCP6_INFORMATION_REQUEST = 11,
DHCP6_RELAY_FORW = 12,
DHCP6_RELAY_REPL = 13,
_DHCP6_MESSAGE_MAX = 14,
};
/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-1 */
typedef enum DHCP6MessageType {
DHCP6_MESSAGE_SOLICIT = 1, /* RFC 8415 */
DHCP6_MESSAGE_ADVERTISE = 2, /* RFC 8415 */
DHCP6_MESSAGE_REQUEST = 3, /* RFC 8415 */
DHCP6_MESSAGE_CONFIRM = 4, /* RFC 8415 */
DHCP6_MESSAGE_RENEW = 5, /* RFC 8415 */
DHCP6_MESSAGE_REBIND = 6, /* RFC 8415 */
DHCP6_MESSAGE_REPLY = 7, /* RFC 8415 */
DHCP6_MESSAGE_RELEASE = 8, /* RFC 8415 */
DHCP6_MESSAGE_DECLINE = 9, /* RFC 8415 */
DHCP6_MESSAGE_RECONFIGURE = 10, /* RFC 8415 */
DHCP6_MESSAGE_INFORMATION_REQUEST = 11, /* RFC 8415 */
DHCP6_MESSAGE_RELAY_FORWARD = 12, /* RFC 8415 */
DHCP6_MESSAGE_RELAY_REPLY = 13, /* RFC 8415 */
DHCP6_MESSAGE_LEASE_QUERY = 14, /* RFC 5007 */
DHCP6_MESSAGE_LEASE_QUERY_REPLY = 15, /* RFC 5007 */
DHCP6_MESSAGE_LEASE_QUERY_DONE = 16, /* RFC 5460 */
DHCP6_MESSAGE_LEASE_QUERY_DATA = 17, /* RFC 5460 */
DHCP6_MESSAGE_RECONFIGURE_REQUEST = 18, /* RFC 6977 */
DHCP6_MESSAGE_RECONFIGURE_REPLY = 19, /* RFC 6977 */
DHCP6_MESSAGE_DHCPV4_QUERY = 20, /* RFC 7341 */
DHCP6_MESSAGE_DHCPV4_RESPONSE = 21, /* RFC 7341 */
DHCP6_MESSAGE_ACTIVE_LEASE_QUERY = 22, /* RFC 7653 */
DHCP6_MESSAGE_START_TLS = 23, /* RFC 7653 */
DHCP6_MESSAGE_BINDING_UPDATE = 24, /* RFC 8156 */
DHCP6_MESSAGE_BINDING_REPLY = 25, /* RFC 8156 */
DHCP6_MESSAGE_POOL_REQUEST = 26, /* RFC 8156 */
DHCP6_MESSAGE_POOL_RESPONSE = 27, /* RFC 8156 */
DHCP6_MESSAGE_UPDATE_REQUEST = 28, /* RFC 8156 */
DHCP6_MESSAGE_UPDATE_REQUEST_ALL = 29, /* RFC 8156 */
DHCP6_MESSAGE_UPDATE_DONE = 30, /* RFC 8156 */
DHCP6_MESSAGE_CONNECT = 31, /* RFC 8156 */
DHCP6_MESSAGE_CONNECT_REPLY = 32, /* RFC 8156 */
DHCP6_MESSAGE_DISCONNECT = 33, /* RFC 8156 */
DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
_DHCP6_MESSAGE_TYPE_MAX,
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
} DHCP6MessageType;
enum {
typedef enum DHCP6NTPSubOption {
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
};
} DHCP6NTPSubOption;
/*
* RFC 8415, RFC 5007 and RFC 7653 status codes:
* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
*/
enum {
typedef enum DHCP6Status {
DHCP6_STATUS_SUCCESS = 0,
DHCP6_STATUS_UNSPEC_FAIL = 1,
DHCP6_STATUS_NO_ADDRS_AVAIL = 2,
@ -110,11 +137,12 @@ enum {
DHCP6_STATUS_SERVER_SHUTTING_DOWN = 20,
DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21,
DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22,
_DHCP6_STATUS_MAX = 23,
};
_DHCP6_STATUS_MAX,
_DHCP6_STATUS_INVALID = -EINVAL,
} DHCP6Status;
enum {
DHCP6_FQDN_FLAG_S = (1 << 0),
DHCP6_FQDN_FLAG_O = (1 << 1),
DHCP6_FQDN_FLAG_N = (1 << 2),
};
typedef enum DHCP6FQDNFlag {
DHCP6_FQDN_FLAG_S = 1 << 0,
DHCP6_FQDN_FLAG_O = 1 << 1,
DHCP6_FQDN_FLAG_N = 1 << 2,
} DHCP6FQDNFlag;

View File

@ -47,7 +47,7 @@ int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
return CMP(x->until, y->until);
}
_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
if (!n)
return NULL;
@ -72,7 +72,7 @@ static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
return mfree(n);
}
_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
/* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
* the sd_lldp object. */
@ -360,7 +360,7 @@ bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
}
_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
assert_return(n, -EINVAL);
assert_return(address, -EINVAL);
@ -368,7 +368,7 @@ _public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct eth
return 0;
}
_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
assert_return(n, -EINVAL);
assert_return(address, -EINVAL);
@ -376,7 +376,7 @@ _public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struc
return 0;
}
_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
assert_return(size, -EINVAL);
@ -387,7 +387,7 @@ _public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, siz
return 0;
}
_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
assert_return(ret, -EINVAL);
@ -440,7 +440,7 @@ static int format_network_address(const void *data, size_t sz, char **ret) {
return 1;
}
_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
char *k;
int r;
@ -496,7 +496,7 @@ done:
return 0;
}
_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
assert_return(ret, -EINVAL);
@ -511,7 +511,7 @@ _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, co
return 0;
}
_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
char *k;
int r;
@ -566,7 +566,7 @@ done:
return 0;
}
_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
assert_return(n, -EINVAL);
assert_return(ret_sec, -EINVAL);
@ -574,7 +574,7 @@ _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
return 0;
}
_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
@ -585,7 +585,7 @@ _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **
return 0;
}
_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
@ -596,7 +596,7 @@ _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const
return 0;
}
_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
@ -607,7 +607,7 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch
return 0;
}
_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
@ -618,7 +618,7 @@ _public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret)
return 0;
}
_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
@ -629,7 +629,7 @@ _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint1
return 0;
}
_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
assert_return(n, -EINVAL);
assert_return(ret, -EINVAL);
@ -640,7 +640,7 @@ _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint
return 0;
}
_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
int r;
@ -661,7 +661,7 @@ _public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw,
return r;
}
_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
assert_return(n, -EINVAL);
assert(n->raw_size >= sizeof(struct ether_header));
@ -670,7 +670,7 @@ _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
return n->rindex < n->raw_size;
}
_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
size_t length;
assert_return(n, -EINVAL);
@ -689,7 +689,7 @@ _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
return n->rindex < n->raw_size;
}
_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
@ -703,7 +703,7 @@ _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
return 0;
}
_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
uint8_t k;
int r;
@ -716,7 +716,7 @@ _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
return type == k;
}
_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
const uint8_t *d;
size_t length;
int r;
@ -745,7 +745,7 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_A
return 0;
}
_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
uint8_t k[3], st;
int r;
@ -758,7 +758,7 @@ _public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[
return memcmp(k, oui, 3) == 0 && st == subtype;
}
_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
size_t length;
assert_return(n, -EINVAL);
@ -780,7 +780,7 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
return 0;
}
_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
assert_return(n, -EINVAL);
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
assert_return(clock_supported(clock), -EOPNOTSUPP);

View File

@ -11,7 +11,6 @@
#include "socket-util.h"
int lldp_network_bind_raw_socket(int ifindex) {
static const struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */
@ -26,26 +25,21 @@ int lldp_network_bind_raw_socket(int ifindex) {
BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept packet */
};
static const struct sock_fprog fprog = {
.len = ELEMENTSOF(filter),
.filter = (struct sock_filter*) filter,
};
struct packet_mreq mreq = {
.mr_ifindex = ifindex,
.mr_type = PACKET_MR_MULTICAST,
.mr_alen = ETH_ALEN,
.mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
};
union sockaddr_union saddrll = {
.ll.sll_family = AF_PACKET,
.ll.sll_ifindex = ifindex,
};
_cleanup_close_ int fd = -1;
int r;
assert(ifindex > 0);
@ -54,29 +48,24 @@ int lldp_network_bind_raw_socket(int ifindex) {
if (fd < 0)
return -errno;
r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0)
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
return -errno;
/* customer bridge */
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (r < 0)
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
return -errno;
/* non TPMR bridge */
mreq.mr_address[ETH_ALEN - 1] = 0x03;
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (r < 0)
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
return -errno;
/* nearest bridge */
mreq.mr_address[ETH_ALEN - 1] = 0x0E;
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
if (r < 0)
if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
return -errno;
r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
if (r < 0)
if (bind(fd, &saddrll.sa, sizeof(saddrll.ll)) < 0)
return -errno;
return TAKE_FD(fd);

View File

@ -8,8 +8,10 @@
int _e = (error); \
if (DEBUG_LOGGING) { \
const char *_n = NULL; \
type *_v = (val); \
\
(void) type##_get_ifname(val, &_n); \
if (_v) \
(void) type##_get_ifname(_v, &_n); \
log_interface_full_errno_zerook( \
_n, LOG_DEBUG, _e, prefix fmt, \
##__VA_ARGS__); \

View File

@ -837,7 +837,8 @@ static int client_message_init(
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
client->arp_type, optlen, &optoffset);
client->arp_type, client->mac_addr_len, client->mac_addr,
optlen, &optoffset);
if (r < 0)
return r;
@ -853,7 +854,7 @@ static int client_message_init(
secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
packet->dhcp.secs = htobe16(secs);
/* RFC2132 section 4.1
/* RFC2131 section 4.1
A client that cannot receive unicast IP datagrams until its protocol
software has been configured with an IP address SHOULD set the
BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
@ -867,15 +868,6 @@ static int client_message_init(
if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
packet->dhcp.flags = htobe16(0x8000);
/* RFC2132 section 4.1.1:
The client MUST include its hardware address in the chaddr field, if
necessary for delivery of DHCP reply messages. Non-Ethernet
interfaces will leave 'chaddr' empty and use the client identifier
instead (eg, RFC 4390 section 2.1).
*/
if (client->arp_type == ARPHRD_ETHER)
memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
/* If no client identifier exists, construct an RFC 4361-compliant one */
if (client->client_id_len == 0) {
size_t duid_len;
@ -1935,13 +1927,13 @@ static int client_receive_message_udp(
assert(client);
buflen = next_datagram_size_fd(fd);
if (buflen == -ENETDOWN)
/* the link is down. Don't return an error or the I/O event
source will be disconnected and we won't be able to receive
packets again when the link comes back. */
if (buflen < 0) {
if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
return 0;
log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0;
if (buflen < 0)
return buflen;
}
message = malloc0(buflen);
if (!message)
@ -1949,12 +1941,11 @@ static int client_receive_message_udp(
len = recv(fd, message, buflen, 0);
if (len < 0) {
/* see comment above for why we shouldn't error out on ENETDOWN. */
if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
return 0;
return log_dhcp_client_errno(client, errno,
"Could not receive message from UDP socket: %m");
log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
if ((size_t) len < sizeof(DHCPMessage)) {
log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
@ -2000,7 +1991,9 @@ static int client_receive_message_udp(
return 0;
}
return client_handle_message(client, message, len);
log_dhcp_client(client, "Received message from UDP socket, processing.");
(void) client_handle_message(client, message, len);
return 0;
}
static int client_receive_message_raw(
@ -2028,10 +2021,13 @@ static int client_receive_message_raw(
assert(client);
buflen = next_datagram_size_fd(fd);
if (buflen == -ENETDOWN)
if (buflen < 0) {
if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
return 0;
log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0;
if (buflen < 0)
return buflen;
}
packet = malloc0(buflen);
if (!packet)
@ -2040,12 +2036,13 @@ static int client_receive_message_raw(
iov = IOVEC_MAKE(packet, buflen);
len = recvmsg_safe(fd, &msg, 0);
if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN))
return 0;
if (len < 0)
return log_dhcp_client_errno(client, len,
"Could not receive message from raw socket: %m");
if (len < 0) {
if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
return 0;
log_dhcp_client_errno(client, len, "Could not receive message from raw socket, ignoring: %m");
return 0;
}
if ((size_t) len < sizeof(DHCPPacket))
return 0;
@ -2061,7 +2058,9 @@ static int client_receive_message_raw(
len -= DHCP_IP_UDP_SIZE;
return client_handle_message(client, &packet->dhcp, len);
log_dhcp_client(client, "Received message from RAW socket, processing.");
(void) client_handle_message(client, &packet->dhcp, len);
return 0;
}
int sd_dhcp_client_send_renew(sd_dhcp_client *client) {

View File

@ -107,12 +107,13 @@ int sd_dhcp_lease_get_servers(
assert_return(lease, -EINVAL);
assert_return(what >= 0, -EINVAL);
assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
assert_return(addr, -EINVAL);
if (lease->servers[what].size <= 0)
return -ENODATA;
*addr = lease->servers[what].addr;
if (addr)
*addr = lease->servers[what].addr;
return (int) lease->servers[what].size;
}
@ -252,6 +253,33 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
return -ENODATA;
}
int sd_dhcp_lease_get_6rd(
sd_dhcp_lease *lease,
uint8_t *ret_ipv4masklen,
uint8_t *ret_prefixlen,
struct in6_addr *ret_prefix,
const struct in_addr **ret_br_addresses,
size_t *ret_n_br_addresses) {
assert_return(lease, -EINVAL);
if (lease->sixrd_n_br_addresses <= 0)
return -ENODATA;
if (ret_ipv4masklen)
*ret_ipv4masklen = lease->sixrd_ipv4masklen;
if (ret_prefixlen)
*ret_prefixlen = lease->sixrd_prefixlen;
if (ret_prefix)
*ret_prefix = lease->sixrd_prefix;
if (ret_br_addresses)
*ret_br_addresses = lease->sixrd_br_addresses;
if (ret_n_br_addresses)
*ret_n_br_addresses = lease->sixrd_n_br_addresses;
return 0;
}
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
assert_return(lease, -EINVAL);
assert_return(data, -EINVAL);
@ -290,6 +318,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
free(lease->client_id);
free(lease->vendor_specific);
strv_free(lease->search_domains);
free(lease->sixrd_br_addresses);
return mfree(lease);
}
@ -535,6 +564,61 @@ static int lease_parse_classless_routes(
return 0;
}
static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
uint8_t ipv4masklen, prefixlen;
struct in6_addr prefix;
_cleanup_free_ struct in_addr *br_addresses = NULL;
size_t n_br_addresses;
assert(lease);
assert(option);
/* See RFC 5969 Section 7.1.1 */
if (lease->sixrd_n_br_addresses > 0)
/* Multiple 6rd option?? */
return -EINVAL;
/* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
(len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
return -EINVAL;
/* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
* within a given 6rd domain. This may be any value between 0 and 32. Any value
* greater than 32 is invalid. */
ipv4masklen = option[0];
if (ipv4masklen > 32)
return -EINVAL;
/* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
* purpose of bounds checking by DHCP option processing, the sum of
* (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
prefixlen = option[1];
if (32 - ipv4masklen + prefixlen > 128)
return -EINVAL;
/* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
* The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
* MUST be initialized to zero by the sender and ignored by the receiver. */
memcpy(&prefix, option + 2, sizeof(struct in6_addr));
(void) in6_addr_mask(&prefix, prefixlen);
/* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relay(s) for a given 6rd domain. */
n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
if (!br_addresses)
return -ENOMEM;
lease->sixrd_ipv4masklen = ipv4masklen;
lease->sixrd_prefixlen = prefixlen;
lease->sixrd_prefix = prefix;
lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
lease->sixrd_n_br_addresses = n_br_addresses;
return 0;
}
int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
sd_dhcp_lease *lease = userdata;
int r;
@ -693,7 +777,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
}
if (!timezone_is_valid(tz, LOG_DEBUG)) {
log_debug_errno(r, "Timezone is not valid, ignoring: %m");
log_debug("Timezone is not valid, ignoring.");
return 0;
}
@ -720,6 +804,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
lease->vendor_specific_len = len;
break;
case SD_DHCP_OPTION_6RD:
r = lease_parse_6rd(lease, option, len);
if (r < 0)
log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
break;
case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
r = dhcp_lease_insert_private_option(lease, code, option, len);
if (r < 0)

View File

@ -27,6 +27,7 @@
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "io-util.h"
#include "network-common.h"
#include "random-util.h"
#include "socket-util.h"
@ -41,16 +42,16 @@
#define IRT_MINIMUM (600 * USEC_PER_SEC)
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
enum {
DHCP6_REQUEST_IA_NA = 1,
DHCP6_REQUEST_IA_TA = 2, /* currently not used */
DHCP6_REQUEST_IA_PD = 4,
};
typedef enum DHCP6RequestIA {
DHCP6_REQUEST_IA_NA = 1 << 0,
DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
DHCP6_REQUEST_IA_PD = 1 << 2,
} DHCP6RequestIA;
struct sd_dhcp6_client {
unsigned n_ref;
enum DHCP6State state;
DHCP6State state;
sd_event *event;
int event_priority;
int ifindex;
@ -64,7 +65,7 @@ struct sd_dhcp6_client {
DHCP6IA ia_pd;
sd_event_source *timeout_t1;
sd_event_source *timeout_t2;
unsigned request;
DHCP6RequestIA request_ia;
be32_t transaction_id;
usec_t transaction_start;
struct sd_dhcp6_lease *lease;
@ -102,20 +103,42 @@ static const uint16_t default_req_opts[] = {
SD_DHCP6_OPTION_SNTP_SERVERS,
};
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
[DHCP6_SOLICIT] = "SOLICIT",
[DHCP6_ADVERTISE] = "ADVERTISE",
[DHCP6_REQUEST] = "REQUEST",
[DHCP6_CONFIRM] = "CONFIRM",
[DHCP6_RENEW] = "RENEW",
[DHCP6_REBIND] = "REBIND",
[DHCP6_REPLY] = "REPLY",
[DHCP6_RELEASE] = "RELEASE",
[DHCP6_DECLINE] = "DECLINE",
[DHCP6_RECONFIGURE] = "RECONFIGURE",
[DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
[DHCP6_RELAY_FORW] = "RELAY-FORW",
[DHCP6_RELAY_REPL] = "RELAY-REPL",
const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
[DHCP6_MESSAGE_SOLICIT] = "Solicit",
[DHCP6_MESSAGE_ADVERTISE] = "Advertise",
[DHCP6_MESSAGE_REQUEST] = "Request",
[DHCP6_MESSAGE_CONFIRM] = "Confirm",
[DHCP6_MESSAGE_RENEW] = "Renew",
[DHCP6_MESSAGE_REBIND] = "Rebind",
[DHCP6_MESSAGE_REPLY] = "Reply",
[DHCP6_MESSAGE_RELEASE] = "Release",
[DHCP6_MESSAGE_DECLINE] = "Decline",
[DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure",
[DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
[DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward",
[DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply",
[DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query",
[DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply",
[DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done",
[DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data",
[DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request",
[DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply",
[DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query",
[DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response",
[DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query",
[DHCP6_MESSAGE_START_TLS] = "Start TLS",
[DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update",
[DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply",
[DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request",
[DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response",
[DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request",
[DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All",
[DHCP6_MESSAGE_UPDATE_DONE] = "Update Done",
[DHCP6_MESSAGE_CONNECT] = "Connect",
[DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply",
[DHCP6_MESSAGE_DISCONNECT] = "Disconnect",
[DHCP6_MESSAGE_STATE] = "State",
[DHCP6_MESSAGE_CONTACT] = "Contact",
};
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
@ -151,7 +174,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
#define DHCP6_CLIENT_DONT_DESTROY(client) \
_cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
static int client_start(sd_dhcp6_client *client, DHCP6State state);
int sd_dhcp6_client_set_callback(
sd_dhcp6_client *client,
@ -234,10 +257,6 @@ int sd_dhcp6_client_set_mac(
return 0;
}
if (client->mac_addr_len == addr_len &&
memcmp(&client->mac_addr, addr, addr_len) == 0)
return 0;
memcpy(&client->mac_addr, addr, addr_len);
client->mac_addr_len = addr_len;
client->arp_type = arp_type;
@ -475,7 +494,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
assert_return(client, -EINVAL);
assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
if (option <= 0 || option >= UINT8_MAX)
if (!dhcp6_option_can_request(option))
return -EINVAL;
for (t = 0; t < client->req_opts_len; t++)
@ -548,7 +567,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati
assert_return(client, -EINVAL);
assert_return(delegation, -EINVAL);
*delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
*delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
return 0;
}
@ -556,7 +575,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
assert_return(client, -EINVAL);
SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
return 0;
}
@ -565,7 +584,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
assert_return(client, -EINVAL);
assert_return(request, -EINVAL);
*request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
*request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
return 0;
}
@ -573,7 +592,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
assert_return(client, -EINVAL);
SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
return 0;
}
@ -678,11 +697,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
switch(client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
message->type = DHCP6_INFORMATION_REQUEST;
message->type = DHCP6_MESSAGE_INFORMATION_REQUEST;
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
@ -691,14 +710,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
break;
case DHCP6_STATE_SOLICITATION:
message->type = DHCP6_SOLICIT;
message->type = DHCP6_MESSAGE_SOLICIT;
r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
if (r < 0)
return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen,
&client->ia_na);
if (r < 0)
@ -713,7 +732,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
@ -738,7 +757,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
return r;
@ -750,9 +769,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
case DHCP6_STATE_RENEW:
if (client->state == DHCP6_STATE_REQUEST)
message->type = DHCP6_REQUEST;
message->type = DHCP6_MESSAGE_REQUEST;
else
message->type = DHCP6_RENEW;
message->type = DHCP6_MESSAGE_RENEW;
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
client->lease->serverid_len,
@ -760,7 +779,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
r = dhcp6_option_append_ia(&opt, &optlen,
&client->lease->ia);
if (r < 0)
@ -775,7 +794,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
@ -799,7 +818,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
@ -808,9 +827,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
break;
case DHCP6_STATE_REBIND:
message->type = DHCP6_REBIND;
message->type = DHCP6_MESSAGE_REBIND;
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
if (r < 0)
return r;
@ -824,7 +843,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (client->mudurl) {
r = dhcp6_option_append(&opt, &optlen,
SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
client->mudurl);
if (r < 0)
return r;
@ -848,7 +867,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
@ -859,6 +878,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
return -EINVAL;
default:
assert_not_reached();
}
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
@ -873,14 +894,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
if (r < 0)
return r;
elapsed_usec = time_now - client->transaction_start;
if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
else
elapsed_time = 0xffff;
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
sizeof(elapsed_time), &elapsed_time);
/* RFC 8415 Section 21.9.
* A client MUST include an Elapsed Time option in messages to indicate how long the client has
* been trying to complete a DHCP message exchange. */
elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_time = htobe16(elapsed_usec);
r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
if (r < 0)
return r;
@ -936,7 +955,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
enum DHCP6State state;
DHCP6State state;
assert(s);
assert(client);
@ -980,7 +999,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
case DHCP6_STATE_SOLICITATION:
if (client->retransmit_count && client->lease) {
if (client->retransmit_count > 0 && client->lease) {
client_start(client, DHCP6_STATE_REQUEST);
return 0;
}
@ -1027,6 +1046,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
case DHCP6_STATE_STOPPED:
case DHCP6_STATE_BOUND:
return 0;
default:
assert_not_reached();
}
if (max_retransmit_count > 0 &&
@ -1051,8 +1072,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
client->retransmit_time += init_retransmit_time / 10;
} else {
if (max_retransmit_time > 0 &&
client->retransmit_time > max_retransmit_time / 2)
assert(max_retransmit_time > 0);
if (client->retransmit_time > max_retransmit_time / 2)
client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
else
client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
@ -1113,7 +1134,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
return 0;
}
static int client_parse_message(
int client_parse_message(
sd_dhcp6_client *client,
DHCP6Message *message,
size_t len,
@ -1121,7 +1142,6 @@ static int client_parse_message(
uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
usec_t irt = IRT_DEFAULT;
bool clientid = false;
int r;
assert(client);
@ -1141,23 +1161,19 @@ static int client_parse_message(
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
if (clientid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids",
if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
dhcp6_message_type_to_string(message->type));
if (optlen != client->duid_len ||
memcmp(&client->duid, optval, optlen) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match",
dhcp6_message_type_to_string(message->type));
clientid = true;
r = dhcp6_lease_set_clientid(lease, optval, optlen);
if (r < 0)
return r;
break;
case SD_DHCP6_OPTION_SERVERID:
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
if (r >= 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
dhcp6_message_type_to_string(message->type));
r = dhcp6_lease_set_serverid(lease, optval, optlen);
@ -1255,35 +1271,35 @@ static int client_parse_message(
case SD_DHCP6_OPTION_DNS_SERVERS:
r = dhcp6_lease_add_dns(lease, optval, optlen);
if (r < 0)
return r;
log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
break;
case SD_DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_add_domains(lease, optval, optlen);
if (r < 0)
return r;
log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
break;
case SD_DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_add_ntp(lease, optval, optlen);
if (r < 0)
return r;
log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
break;
case SD_DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_add_sntp(lease, optval, optlen);
if (r < 0)
return r;
log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
break;
case SD_DHCP6_OPTION_FQDN:
case SD_DHCP6_OPTION_CLIENT_FQDN:
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
if (r < 0)
return r;
log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
break;
@ -1296,8 +1312,15 @@ static int client_parse_message(
}
}
if (!clientid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
uint8_t *clientid;
size_t clientid_len;
if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
if (clientid_len != client->duid_len ||
memcmp(clientid, &client->duid, clientid_len) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
@ -1325,21 +1348,32 @@ static int client_parse_message(
return 0;
}
static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
static int client_receive_reply(
sd_dhcp6_client *client,
DHCP6Message *reply,
size_t len,
const triple_timestamp *t,
const struct in6_addr *server_address) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
bool rapid_commit;
int r;
assert(client);
assert(reply);
assert(t);
if (reply->type != DHCP6_REPLY)
if (reply->type != DHCP6_MESSAGE_REPLY)
return 0;
r = dhcp6_lease_new(&lease);
if (r < 0)
return -ENOMEM;
lease->timestamp = *t;
if (server_address)
lease->server_address = *server_address;
r = client_parse_message(client, reply, len, lease);
if (r < 0)
return r;
@ -1359,18 +1393,32 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
return DHCP6_STATE_BOUND;
}
static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
static int client_receive_advertise(
sd_dhcp6_client *client,
DHCP6Message *advertise,
size_t len,
const triple_timestamp *t,
const struct in6_addr *server_address) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
uint8_t pref_advertise = 0, pref_lease = 0;
int r;
if (advertise->type != DHCP6_ADVERTISE)
assert(client);
assert(advertise);
assert(t);
if (advertise->type != DHCP6_MESSAGE_ADVERTISE)
return 0;
r = dhcp6_lease_new(&lease);
if (r < 0)
return r;
lease->timestamp = *t;
if (server_address)
lease->server_address = *server_address;
r = client_parse_message(client, advertise, len, lease);
if (r < 0)
return r;
@ -1401,7 +1449,22 @@ static int client_receive_message(
sd_dhcp6_client *client = userdata;
DHCP6_CLIENT_DONT_DESTROY(client);
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
struct iovec iov;
union sockaddr_union sa = {};
struct msghdr msg = {
.msg_name = &sa.sa,
.msg_namelen = sizeof(sa),
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
triple_timestamp t = {};
_cleanup_free_ DHCP6Message *message = NULL;
struct in6_addr *server_address = NULL;
ssize_t buflen, len;
int r = 0;
@ -1410,52 +1473,59 @@ static int client_receive_message(
assert(client->event);
buflen = next_datagram_size_fd(fd);
if (buflen == -ENETDOWN)
/* the link is down. Don't return an error or the I/O event
source will be disconnected and we won't be able to receive
packets again when the link comes back. */
if (buflen < 0) {
if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
return 0;
log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0;
if (buflen < 0)
return buflen;
}
message = malloc(buflen);
if (!message)
return -ENOMEM;
len = recv(fd, message, buflen, 0);
iov = IOVEC_MAKE(message, buflen);
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (len < 0) {
/* see comment above for why we shouldn't error out on ENETDOWN. */
if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
return 0;
return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
if ((size_t) len < sizeof(DHCP6Message)) {
log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
return 0;
}
switch(message->type) {
case DHCP6_SOLICIT:
case DHCP6_REQUEST:
case DHCP6_CONFIRM:
case DHCP6_RENEW:
case DHCP6_REBIND:
case DHCP6_RELEASE:
case DHCP6_DECLINE:
case DHCP6_INFORMATION_REQUEST:
case DHCP6_RELAY_FORW:
case DHCP6_RELAY_REPL:
return 0;
/* msg_namelen == 0 happens when running the test-suite over a socketpair */
if (msg.msg_namelen > 0) {
if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
log_dhcp6_client(client, "Received message from invalid source, ignoring.");
return 0;
}
case DHCP6_ADVERTISE:
case DHCP6_REPLY:
case DHCP6_RECONFIGURE:
break;
server_address = &sa.in6.sin6_addr;
}
default:
log_dhcp6_client(client, "Unknown message type %d", message->type);
CMSG_FOREACH(cmsg, &msg) {
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)));
}
if (!triple_timestamp_is_set(&t))
triple_timestamp_get(&t);
if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) {
const char *type_str = dhcp6_message_type_to_string(message->type);
if (type_str)
log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str);
else
log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type);
return 0;
}
@ -1464,7 +1534,7 @@ static int client_receive_message(
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
r = client_receive_reply(client, message, len);
r = client_receive_reply(client, message, len, &t, server_address);
if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0;
@ -1477,7 +1547,7 @@ static int client_receive_message(
break;
case DHCP6_STATE_SOLICITATION:
r = client_receive_advertise(client, message, len);
r = client_receive_advertise(client, message, len, &t, server_address);
if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
return 0;
@ -1493,7 +1563,7 @@ static int client_receive_message(
case DHCP6_STATE_RENEW:
case DHCP6_STATE_REBIND:
r = client_receive_reply(client, message, len);
r = client_receive_reply(client, message, len, &t, server_address);
if (r < 0) {
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
return 0;
@ -1517,6 +1587,8 @@ static int client_receive_message(
case DHCP6_STATE_STOPPED:
return 0;
default:
assert_not_reached();
}
log_dhcp6_client(client, "Recv %s",
@ -1530,14 +1602,14 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
assert_return(client, -EINVAL);
assert_return(client->lease, -EINVAL);
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
*lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
return 0;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
*lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
*lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
@ -1547,7 +1619,7 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
return -ENOMSG;
}
static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
static int client_start(sd_dhcp6_client *client, DHCP6State state) {
int r;
usec_t timeout, time_now;
uint32_t lifetime_t1, lifetime_t2;
@ -1647,6 +1719,8 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
client->state = state;
return 0;
default:
assert_not_reached();
}
client->transaction_id = random_u32() & htobe32(0x00ffffff);
@ -1685,7 +1759,7 @@ int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
}
int sd_dhcp6_client_start(sd_dhcp6_client *client) {
enum DHCP6State state = DHCP6_STATE_SOLICITATION;
DHCP6State state = DHCP6_STATE_SOLICITATION;
int r;
assert_return(client, -EINVAL);
@ -1696,7 +1770,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (client->state != DHCP6_STATE_STOPPED)
return -EBUSY;
if (!client->information_request && !client->request)
if (!client->information_request && client->request_ia == 0)
return -EINVAL;
r = client_reset(client);
@ -1824,7 +1898,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
.ia_na.type = SD_DHCP6_OPTION_IA_NA,
.ia_pd.type = SD_DHCP6_OPTION_IA_PD,
.ifindex = -1,
.request = DHCP6_REQUEST_IA_NA,
.request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
.fd = -1,
.req_opts_len = ELEMENTSOF(default_req_opts),
.hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,

View File

@ -13,6 +13,27 @@
#include "strv.h"
#include "util.h"
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
assert_return(lease, -EINVAL);
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
assert_return(clock_supported(clock), -EOPNOTSUPP);
assert_return(ret, -EINVAL);
if (!triple_timestamp_is_set(&lease->timestamp))
return -ENODATA;
*ret = triple_timestamp_by_clock(&lease->timestamp, clock);
return 0;
}
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
*ret = lease->server_address;
return 0;
}
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
DHCP6Address *addr;
uint32_t valid = 0, t;
@ -52,12 +73,43 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
return NULL;
}
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
size_t len) {
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
uint8_t *clientid;
assert_return(lease, -EINVAL);
assert_return(id, -EINVAL);
assert_return(len > 0, -EINVAL);
clientid = memdup(id, len);
if (!clientid)
return -ENOMEM;
free_and_replace(lease->clientid, clientid);
lease->clientid_len = len;
return 0;
}
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
assert_return(lease, -EINVAL);
if (!lease->clientid)
return -ENODATA;
if (ret_id)
*ret_id = lease->clientid;
if (ret_len)
*ret_len = lease->clientid_len;
return 0;
}
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
uint8_t *serverid;
assert_return(lease, -EINVAL);
assert_return(id, -EINVAL);
assert_return(len > 0, -EINVAL);
serverid = memdup(id, len);
if (!serverid)
@ -69,16 +121,16 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
return 0;
}
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) {
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
assert_return(lease, -EINVAL);
if (!lease->serverid)
return -ENOMSG;
return -ENODATA;
if (id)
*id = lease->serverid;
if (len)
*len = lease->serverid_len;
if (ret_id)
*ret_id = lease->serverid;
if (ret_len)
*ret_len = lease->serverid_len;
return 0;
}
@ -119,24 +171,6 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
return 0;
}
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
*iaid = lease->ia.ia_na.id;
return 0;
}
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
assert_return(lease, -EINVAL);
assert_return(iaid, -EINVAL);
*iaid = lease->pd.ia_pd.id;
return 0;
}
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid) {
@ -207,12 +241,13 @@ int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->dns)
return -ENOENT;
*ret = lease->dns;
if (ret)
*ret = lease->dns;
return lease->dns_count;
}
@ -305,16 +340,17 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (lease->ntp) {
*ret = lease->ntp;
if (ret)
*ret = lease->ntp;
return lease->ntp_count;
}
if (lease->sntp && !lease->ntp_fqdn) {
/* Fallback to the deprecated SNTP option. */
*ret = lease->sntp;
if (ret)
*ret = lease->sntp;
return lease->sntp_count;
}
@ -323,12 +359,12 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->ntp_fqdn)
return -ENOENT;
*ret = lease->ntp_fqdn;
if (ret)
*ret = lease->ntp_fqdn;
return strv_length(lease->ntp_fqdn);
}
@ -366,6 +402,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
if (!lease)
return NULL;
free(lease->clientid);
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
dhcp6_lease_free_ia(&lease->pd);

View File

@ -202,6 +202,9 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
space = next_datagram_size_fd(fd);
if (space < 0) {
if (ERRNO_IS_TRANSIENT(space) || ERRNO_IS_DISCONNECT(space))
return 0;
log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
return 0;
}
@ -214,7 +217,7 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
if (length < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
return 0;
log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
@ -251,7 +254,7 @@ int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
return lldp_rx->fd >= 0;
}
_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
int r;
assert_return(lldp_rx, -EINVAL);
@ -285,7 +288,7 @@ fail:
return r;
}
_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
if (!sd_lldp_rx_is_running(lldp_rx))
return 0;
@ -297,7 +300,7 @@ _public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
return 1;
}
_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
int r;
assert_return(lldp_rx, -EINVAL);
@ -317,7 +320,7 @@ _public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64
return 0;
}
_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
assert_return(lldp_rx, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
@ -327,13 +330,13 @@ _public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
return 0;
}
_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
assert_return(lldp_rx, NULL);
return lldp_rx->event;
}
_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
assert_return(lldp_rx, -EINVAL);
lldp_rx->callback = cb;
@ -342,7 +345,7 @@ _public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t
return 0;
}
_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
assert_return(lldp_rx, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
@ -394,7 +397,7 @@ static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
_public_ int sd_lldp_rx_new(sd_lldp_rx **ret) {
int sd_lldp_rx_new(sd_lldp_rx **ret) {
_cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
assert_return(ret, -EINVAL);
@ -462,7 +465,7 @@ static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_nei
return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
}
_public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
_cleanup_free_ sd_lldp_neighbor **l = NULL;
sd_lldp_neighbor *n;
int k = 0;
@ -491,7 +494,7 @@ _public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***r
return k;
}
_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
assert_return(lldp_rx, -EINVAL);
assert_return(m > 0, -EINVAL);
@ -501,7 +504,7 @@ _public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
return 0;
}
_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
assert_return(lldp_rx, -EINVAL);
assert_return(mask != 0, -EINVAL);
@ -510,7 +513,7 @@ _public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
return 0;
}
_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
assert_return(lldp_rx, -EINVAL);
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so

View File

@ -71,6 +71,7 @@ struct sd_event_source {
uint64_t prepare_iteration;
sd_event_destroy_t destroy_callback;
sd_event_handler_t ratelimit_expire_callback;
LIST_FIELDS(sd_event_source, sources);
@ -214,6 +215,11 @@ struct inotify_data {
* the events locally if they can't be coalesced). */
unsigned n_pending;
/* If this counter is non-zero, don't GC the inotify data object even if not used to watch any inode
* anymore. This is useful to pin the object for a bit longer, after the last event source needing it
* is gone. */
unsigned n_busy;
/* A linked list of all inotify objects with data already read, that still need processing. We keep this list
* to make it efficient to figure out what inotify objects to process data on next. */
LIST_FIELDS(struct inotify_data, buffered);

View File

@ -86,6 +86,30 @@ int event_reset_time(
return created;
}
int event_reset_time_relative(
sd_event *e,
sd_event_source **s,
clockid_t clock,
uint64_t usec,
uint64_t accuracy,
sd_event_time_handler_t callback,
void *userdata,
int64_t priority,
const char *description,
bool force_reset) {
usec_t usec_now;
int r;
assert(e);
r = sd_event_now(e, clock, &usec_now);
if (r < 0)
return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
}
int event_source_disable(sd_event_source *s) {
if (!s)
return 0;

View File

@ -5,9 +5,27 @@
#include "sd-event.h"
int event_reset_time(sd_event *e, sd_event_source **s,
clockid_t clock, uint64_t usec, uint64_t accuracy,
sd_event_time_handler_t callback, void *userdata,
int64_t priority, const char *description, bool force_reset);
int event_reset_time(
sd_event *e,
sd_event_source **s,
clockid_t clock,
uint64_t usec,
uint64_t accuracy,
sd_event_time_handler_t callback,
void *userdata,
int64_t priority,
const char *description,
bool force_reset);
int event_reset_time_relative(
sd_event *e,
sd_event_source **s,
clockid_t clock,
uint64_t usec,
uint64_t accuracy,
sd_event_time_handler_t callback,
void *userdata,
int64_t priority,
const char *description,
bool force_reset);
int event_source_disable(sd_event_source *s);
int event_source_is_enabled(sd_event_source *s);

View File

@ -1824,6 +1824,29 @@ static void event_free_inode_data(
free(d);
}
static void event_gc_inotify_data(
sd_event *e,
struct inotify_data *d) {
assert(e);
/* GCs the inotify data object if we don't need it anymore. That's the case if we don't want to watch
* any inode with it anymore, which in turn happens if no event source of this priority is interested
* in any inode any longer. That said, we maintain an extra busy counter: if non-zero we'll delay GC
* (under the expectation that the GC is called again once the counter is decremented). */
if (!d)
return;
if (!hashmap_isempty(d->inodes))
return;
if (d->n_busy > 0)
return;
event_free_inotify_data(e, d);
}
static void event_gc_inode_data(
sd_event *e,
struct inode_data *d) {
@ -1841,8 +1864,7 @@ static void event_gc_inode_data(
inotify_data = d->inotify_data;
event_free_inode_data(e, d);
if (inotify_data && hashmap_isempty(inotify_data->inodes))
event_free_inotify_data(e, inotify_data);
event_gc_inotify_data(e, inotify_data);
}
static int event_make_inode_data(
@ -1971,24 +1993,25 @@ static int inotify_exit_callback(sd_event_source *s, const struct inotify_event
return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
}
_public_ int sd_event_add_inotify(
static int event_add_inotify_fd_internal(
sd_event *e,
sd_event_source **ret,
const char *path,
int fd,
bool donate,
uint32_t mask,
sd_event_inotify_handler_t callback,
void *userdata) {
_cleanup_close_ int donated_fd = donate ? fd : -1;
_cleanup_(source_freep) sd_event_source *s = NULL;
struct inotify_data *inotify_data = NULL;
struct inode_data *inode_data = NULL;
_cleanup_close_ int fd = -1;
_cleanup_(source_freep) sd_event_source *s = NULL;
struct stat st;
int r;
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(path, -EINVAL);
assert_return(fd >= 0, -EBADF);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@ -2001,12 +2024,6 @@ _public_ int sd_event_add_inotify(
if (mask & IN_MASK_ADD)
return -EINVAL;
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;
if (fstat(fd, &st) < 0)
return -errno;
@ -2026,14 +2043,24 @@ _public_ int sd_event_add_inotify(
r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data);
if (r < 0) {
event_free_inotify_data(e, inotify_data);
event_gc_inotify_data(e, inotify_data);
return r;
}
/* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of
* the event source, until then, for which we need the original inode. */
if (inode_data->fd < 0) {
inode_data->fd = TAKE_FD(fd);
if (donated_fd >= 0)
inode_data->fd = TAKE_FD(donated_fd);
else {
inode_data->fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
if (inode_data->fd < 0) {
r = -errno;
event_gc_inode_data(e, inode_data);
return r;
}
}
LIST_PREPEND(to_close, e->inode_data_to_close, inode_data);
}
@ -2046,8 +2073,6 @@ _public_ int sd_event_add_inotify(
if (r < 0)
return r;
(void) sd_event_source_set_description(s, path);
if (ret)
*ret = s;
TAKE_PTR(s);
@ -2055,6 +2080,48 @@ _public_ int sd_event_add_inotify(
return 0;
}
_public_ int sd_event_add_inotify_fd(
sd_event *e,
sd_event_source **ret,
int fd,
uint32_t mask,
sd_event_inotify_handler_t callback,
void *userdata) {
return event_add_inotify_fd_internal(e, ret, fd, /* donate= */ false, mask, callback, userdata);
}
_public_ int sd_event_add_inotify(
sd_event *e,
sd_event_source **ret,
const char *path,
uint32_t mask,
sd_event_inotify_handler_t callback,
void *userdata) {
sd_event_source *s = NULL; /* avoid false maybe-uninitialized warning */
int fd, r;
assert_return(path, -EINVAL);
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;
r = event_add_inotify_fd_internal(e, &s, fd, /* donate= */ true, mask, callback, userdata);
if (r < 0)
return r;
(void) sd_event_source_set_description(s, path);
if (ret)
*ret = s;
return r;
}
static sd_event_source* event_source_free(sd_event_source *s) {
if (!s)
return NULL;
@ -2845,7 +2912,7 @@ fail:
return r;
}
static int event_source_leave_ratelimit(sd_event_source *s) {
static int event_source_leave_ratelimit(sd_event_source *s, bool run_callback) {
int r;
assert(s);
@ -2877,6 +2944,30 @@ static int event_source_leave_ratelimit(sd_event_source *s) {
ratelimit_reset(&s->rate_limit);
log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
if (run_callback && s->ratelimit_expire_callback) {
s->dispatching = true;
r = s->ratelimit_expire_callback(s, s->userdata);
s->dispatching = false;
if (r < 0) {
log_debug_errno(r, "Ratelimit expiry callback of event source %s (type %s) returned error, %s: %m",
strna(s->description),
event_source_type_to_string(s->type),
s->exit_on_failure ? "exiting" : "disabling");
if (s->exit_on_failure)
(void) sd_event_exit(s->event, r);
}
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
return 1;
}
return 0;
fail:
@ -3055,7 +3146,7 @@ static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
ss = read(fd, &x, sizeof(x));
if (ss < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
if (ERRNO_IS_TRANSIENT(errno))
return 0;
return -errno;
@ -3076,6 +3167,7 @@ static int process_timer(
struct clock_data *d) {
sd_event_source *s;
bool callback_invoked = false;
int r;
assert(e);
@ -3093,9 +3185,11 @@ static int process_timer(
* again. */
assert(s->ratelimited);
r = event_source_leave_ratelimit(s);
r = event_source_leave_ratelimit(s, /* run_callback */ true);
if (r < 0)
return r;
else if (r == 1)
callback_invoked = true;
continue;
}
@ -3110,7 +3204,7 @@ static int process_timer(
event_source_time_prioq_reshuffle(s);
}
return 0;
return callback_invoked;
}
static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) {
@ -3258,7 +3352,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i
n = read(d->fd, &si, sizeof(si));
if (n < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
if (ERRNO_IS_TRANSIENT(errno))
return 0;
return -errno;
@ -3312,7 +3406,7 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t
n = read(d->fd, &d->buffer, sizeof(d->buffer));
if (n < 0) {
if (IN_SET(errno, EAGAIN, EINTR))
if (ERRNO_IS_TRANSIENT(errno))
return 0;
return -errno;
@ -3560,13 +3654,23 @@ static int source_dispatch(sd_event_source *s) {
sz = offsetof(struct inotify_event, name) + d->buffer.ev.len;
assert(d->buffer_filled >= sz);
/* If the inotify callback destroys the event source then this likely means we don't need to
* watch the inode anymore, and thus also won't need the inotify object anymore. But if we'd
* free it immediately, then we couldn't drop the event from the inotify event queue without
* memory corruption anymore, as below. Hence, let's not free it immediately, but mark it
* "busy" with a counter (which will ensure it's not GC'ed away prematurely). Let's then
* explicitly GC it after we are done dropping the inotify event from the buffer. */
d->n_busy++;
r = s->inotify.callback(s, &d->buffer.ev, s->userdata);
d->n_busy--;
/* When no event is pending anymore on this inotify object, then let's drop the event from the
* buffer. */
/* When no event is pending anymore on this inotify object, then let's drop the event from
* the inotify event queue buffer. */
if (d->n_pending == 0)
event_inotify_data_drop(e, d, sz);
/* Now we don't want to access 'd' anymore, it's OK to GC now. */
event_gc_inotify_data(e, d);
break;
}
@ -3591,7 +3695,7 @@ static int source_dispatch(sd_event_source *s) {
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
sd_event_source_set_enabled(s, SD_EVENT_OFF);
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
return 1;
}
@ -3632,7 +3736,7 @@ static int event_prepare(sd_event *e) {
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
sd_event_source_set_enabled(s, SD_EVENT_OFF);
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
}
return 0;
@ -3693,10 +3797,7 @@ static int arm_watchdog(sd_event *e) {
if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
its.it_value.tv_nsec = 1;
if (timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL) < 0)
return -errno;
return 0;
return RET_NERRNO(timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL));
}
static int process_watchdog(sd_event *e) {
@ -3806,7 +3907,7 @@ static int epoll_wait_usec(
int maxevents,
usec_t timeout) {
int r, msec;
int msec;
#if 0
static bool epoll_pwait2_absent = false;
@ -3846,14 +3947,7 @@ static int epoll_wait_usec(
msec = (int) k;
}
r = epoll_wait(fd,
events,
maxevents,
msec);
if (r < 0)
return -errno;
return r;
return RET_NERRNO(epoll_wait(fd, events, maxevents, msec));
}
static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) {
@ -4024,6 +4118,10 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0)
goto finish;
r = process_inotify(e);
if (r < 0)
goto finish;
r = process_timer(e, e->timestamp.realtime, &e->realtime);
if (r < 0)
goto finish;
@ -4032,10 +4130,6 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0)
goto finish;
r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
if (r < 0)
goto finish;
r = process_timer(e, e->timestamp.realtime, &e->realtime_alarm);
if (r < 0)
goto finish;
@ -4044,9 +4138,20 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0)
goto finish;
r = process_inotify(e);
r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
if (r < 0)
goto finish;
else if (r == 1) {
/* Ratelimit expiry callback was called. Let's postpone processing pending sources and
* put loop in the initial state in order to evaluate (in the next iteration) also sources
* there were potentially re-enabled by the callback.
*
* Wondering why we treat only this invocation of process_timer() differently? Once event
* source is ratelimited we essentially transform it into CLOCK_MONOTONIC timer hence
* ratelimit expiry callback is never called for any other timer type. */
r = 0;
goto finish;
}
if (event_next_pending(e)) {
e->state = SD_EVENT_PENDING;
@ -4117,7 +4222,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
this_run = now(CLOCK_MONOTONIC);
l = u64log2(this_run - e->last_run_usec);
l = log2u64(this_run - e->last_run_usec);
assert(l < ELEMENTSOF(e->delays));
e->delays[l]++;
@ -4416,7 +4521,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
/* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
* non-ratelimited. */
r = event_source_leave_ratelimit(s);
r = event_source_leave_ratelimit(s, /* run_callback */ false);
if (r < 0)
return r;
@ -4424,6 +4529,13 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
return 0;
}
_public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) {
assert_return(s, -EINVAL);
s->ratelimit_expire_callback = callback;
return 0;
}
_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
assert_return(s, -EINVAL);

View File

@ -224,7 +224,7 @@ int id128_get_product(sd_id128_t *ret) {
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
if (r == -ENOENT)
r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
if (r < 0)
return r;

View File

@ -6,21 +6,14 @@
#include <fcntl.h>
#include <unistd.h>
#if HAVE_OPENSSL
#include <openssl/hmac.h>
#include <openssl/sha.h>
#endif
#include "sd-id128.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "hexdecoct.h"
#include "hmac.h"
#include "id128-util.h"
#include "io-util.h"
#if !HAVE_OPENSSL
#include "khash.h"
#endif
#include "macro.h"
#include "missing_syscall.h"
#include "random-util.h"
@ -283,43 +276,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
}
static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
uint8_t hmac[SHA256_DIGEST_SIZE];
sd_id128_t result;
assert(ret);
#if HAVE_OPENSSL
/* We prefer doing this in-process, since we this means we are not dependent on kernel configuration,
* and this also works in locked down container environments. But some distros don't like OpenSSL's
* license and its (in-) compatibility with GPL2, hence also support khash */
uint8_t md[256/8];
if (!HMAC(EVP_sha256(),
&base, sizeof(base),
(const unsigned char*) &app_id, sizeof(app_id),
md, NULL))
return -ENOTRECOVERABLE;
hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
/* Take only the first half. */
memcpy(&result, md, MIN(sizeof(md), sizeof(result)));
#else
_cleanup_(khash_unrefp) khash *h = NULL;
const void *p;
int r;
r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
if (r < 0)
return r;
r = khash_put(h, &app_id, sizeof(app_id));
if (r < 0)
return r;
r = khash_digest_data(h, &p);
if (r < 0)
return r;
/* We chop off the trailing 16 bytes */
memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
#endif
memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
*ret = id128_make_v4_uuid(result);
return 0;

View File

@ -45,65 +45,66 @@ enum {
};
enum {
SD_DHCP_OPTION_PAD = 0,
SD_DHCP_OPTION_SUBNET_MASK = 1,
SD_DHCP_OPTION_TIME_OFFSET = 2,
SD_DHCP_OPTION_ROUTER = 3,
SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
SD_DHCP_OPTION_LPR_SERVER = 9,
SD_DHCP_OPTION_HOST_NAME = 12,
SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
SD_DHCP_OPTION_DOMAIN_NAME = 15,
SD_DHCP_OPTION_ROOT_PATH = 17,
SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
SD_DHCP_OPTION_POLICY_FILTER = 21,
SD_DHCP_OPTION_INTERFACE_MDR = 22,
SD_DHCP_OPTION_INTERFACE_TTL = 23,
SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
SD_DHCP_OPTION_INTERFACE_MTU = 26,
SD_DHCP_OPTION_BROADCAST = 28,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_ROUTER_DISCOVER = 31,
SD_DHCP_OPTION_STATIC_ROUTE = 33,
SD_DHCP_OPTION_NTP_SERVER = 42,
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NODETYPE = 46,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_SCOPE = 47,
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
SD_DHCP_OPTION_OVERLOAD = 52,
SD_DHCP_OPTION_MESSAGE_TYPE = 53,
SD_DHCP_OPTION_SERVER_IDENTIFIER = 54,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
SD_DHCP_OPTION_ERROR_MESSAGE = 56,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
SD_DHCP_OPTION_RENEWAL_T1_TIME = 58,
SD_DHCP_OPTION_REBINDING_T2_TIME = 59,
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61,
SD_DHCP_OPTION_SMTP_SERVER = 69,
SD_DHCP_OPTION_POP3_SERVER = 70,
SD_DHCP_OPTION_USER_CLASS = 77,
SD_DHCP_OPTION_FQDN = 81,
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82,
SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
SD_DHCP_OPTION_SIP_SERVER = 120,
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
SD_DHCP_OPTION_MUD_URL = 161,
SD_DHCP_OPTION_PRIVATE_BASE = 224,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PAD = 0,
SD_DHCP_OPTION_SUBNET_MASK = 1,
SD_DHCP_OPTION_TIME_OFFSET = 2,
SD_DHCP_OPTION_ROUTER = 3,
SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
SD_DHCP_OPTION_LPR_SERVER = 9,
SD_DHCP_OPTION_HOST_NAME = 12,
SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
SD_DHCP_OPTION_DOMAIN_NAME = 15,
SD_DHCP_OPTION_ROOT_PATH = 17,
SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
SD_DHCP_OPTION_POLICY_FILTER = 21,
SD_DHCP_OPTION_INTERFACE_MDR = 22,
SD_DHCP_OPTION_INTERFACE_TTL = 23,
SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
SD_DHCP_OPTION_INTERFACE_MTU = 26,
SD_DHCP_OPTION_BROADCAST = 28,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_ROUTER_DISCOVER = 31,
SD_DHCP_OPTION_STATIC_ROUTE = 33,
SD_DHCP_OPTION_NTP_SERVER = 42,
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NODETYPE = 46,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_SCOPE = 47,
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
SD_DHCP_OPTION_OVERLOAD = 52,
SD_DHCP_OPTION_MESSAGE_TYPE = 53,
SD_DHCP_OPTION_SERVER_IDENTIFIER = 54,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
SD_DHCP_OPTION_ERROR_MESSAGE = 56,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
SD_DHCP_OPTION_RENEWAL_T1_TIME = 58,
SD_DHCP_OPTION_REBINDING_T2_TIME = 59,
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61,
SD_DHCP_OPTION_SMTP_SERVER = 69,
SD_DHCP_OPTION_POP3_SERVER = 70,
SD_DHCP_OPTION_USER_CLASS = 77,
SD_DHCP_OPTION_FQDN = 81,
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82,
SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
SD_DHCP_OPTION_SIP_SERVER = 120,
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
SD_DHCP_OPTION_MUD_URL = 161,
SD_DHCP_OPTION_6RD = 212,
SD_DHCP_OPTION_PRIVATE_BASE = 224,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
SD_DHCP_OPTION_PRIVATE_LAST = 254,
SD_DHCP_OPTION_END = 255,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
SD_DHCP_OPTION_PRIVATE_LAST = 254,
SD_DHCP_OPTION_END = 255,
};
/* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */

View File

@ -71,6 +71,13 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
int sd_dhcp_lease_get_6rd(
sd_dhcp_lease *lease,
uint8_t *ret_ipv4masklen,
uint8_t *ret_prefixlen,
struct in6_addr *ret_prefix,
const struct in_addr **ret_br_addresses,
size_t *ret_n_br_addresses);
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);

View File

@ -39,44 +39,151 @@ enum {
SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13,
};
/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
enum {
SD_DHCP6_OPTION_CLIENTID = 1,
SD_DHCP6_OPTION_SERVERID = 2,
SD_DHCP6_OPTION_IA_NA = 3,
SD_DHCP6_OPTION_IA_TA = 4,
SD_DHCP6_OPTION_IAADDR = 5,
SD_DHCP6_OPTION_ORO = 6,
SD_DHCP6_OPTION_PREFERENCE = 7,
SD_DHCP6_OPTION_ELAPSED_TIME = 8,
SD_DHCP6_OPTION_RELAY_MSG = 9,
SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
/* option code 10 is unassigned */
SD_DHCP6_OPTION_AUTH = 11,
SD_DHCP6_OPTION_UNICAST = 12,
SD_DHCP6_OPTION_STATUS_CODE = 13,
SD_DHCP6_OPTION_RAPID_COMMIT = 14,
SD_DHCP6_OPTION_USER_CLASS = 15,
SD_DHCP6_OPTION_VENDOR_CLASS = 16,
SD_DHCP6_OPTION_VENDOR_OPTS = 17,
SD_DHCP6_OPTION_INTERFACE_ID = 18,
SD_DHCP6_OPTION_RECONF_MSG = 19,
SD_DHCP6_OPTION_RECONF_ACCEPT = 20,
SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_NIS_SERVERS = 27, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_SERVERS = 28, /* RFC 3898 */
SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */
SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 8415, sec. 21.23 */
SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
/* option code 35 is unassigned */
SD_DHCP6_OPTION_FQDN = 39, /* RFC 4704 */
SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE = 41, /* RFC 4833 */
SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE = 42, /* RFC 4833 */
SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
SD_DHCP6_OPTION_MUD_URL = 112, /* RFC 8250 */
/* option codes 89-142 are unassigned */
SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143, /* RFC 6153 */
/* option codes 144-65535 are unassigned */
};

View File

@ -21,6 +21,7 @@
#include <inttypes.h>
#include <netinet/in.h>
#include <sys/types.h>
#include "_sd-common.h"
@ -28,6 +29,9 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,

View File

@ -93,6 +93,7 @@ int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_sign
int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
int sd_event_add_inotify_fd(sd_event *e, sd_event_source **s, int fd, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
@ -165,6 +166,7 @@ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b);
int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
int sd_event_source_is_ratelimited(sd_event_source *s);
int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback);
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);

View File

@ -82,9 +82,6 @@ int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);
int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret);
int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size);
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);

View File

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View File

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View File

@ -1,3 +1,5 @@
#pragma once
/* dummy header */
#include <linux/magic.h>

View File

@ -0,0 +1,3 @@
#pragma once
/* dummy header */

View File

@ -22,20 +22,25 @@ typedef void (*free_func_t)(void *p);
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
#define alloca_safe(n) \
({ \
size_t _nn_ = n; \
assert(_nn_ <= ALLOCA_MAX); \
alloca(_nn_ == 0 ? 1 : _nn_); \
}) \
#define newa(t, n) \
({ \
size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \
assert(sizeof(t)*_n_ <= ALLOCA_MAX); \
(t*) alloca((sizeof(t)*_n_) ?: 1); \
(t*) alloca_safe(sizeof(t)*_n_); \
})
#define newa0(t, n) \
({ \
size_t _n_ = n; \
assert(!size_multiply_overflow(sizeof(t), _n_)); \
assert(sizeof(t)*_n_ <= ALLOCA_MAX); \
(t*) alloca0((sizeof(t)*_n_) ?: 1); \
(t*) alloca0((sizeof(t)*_n_)); \
})
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
@ -44,12 +49,6 @@ typedef void (*free_func_t)(void *p);
#define malloc0(n) (calloc(1, (n) ?: 1))
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})
#define free_and_replace(a, b) \
({ \
typeof(a)* _a = &(a); \
@ -67,8 +66,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
({ \
void *_q_; \
size_t _l_ = l; \
assert(_l_ <= ALLOCA_MAX); \
_q_ = alloca(_l_ ?: 1); \
_q_ = alloca_safe(_l_); \
memcpy_safe(_q_, p, _l_); \
})
@ -76,8 +74,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
({ \
void *_q_; \
size_t _l_ = l; \
assert(_l_ <= ALLOCA_MAX); \
_q_ = alloca(_l_ + 1); \
_q_ = alloca_safe(_l_ + 1); \
((uint8_t*) _q_)[_l_] = 0; \
memcpy_safe(_q_, p, _l_); \
})
@ -144,9 +141,8 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
({ \
char *_new_; \
size_t _len_ = n; \
assert(_len_ <= ALLOCA_MAX); \
_new_ = alloca(_len_ ?: 1); \
(void *) memset(_new_, 0, _len_); \
_new_ = alloca_safe(_len_); \
memset(_new_, 0, _len_); \
})
/* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
@ -155,8 +151,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
void *_ptr_; \
size_t _mask_ = (align) - 1; \
size_t _size_ = size; \
assert(_size_ <= ALLOCA_MAX); \
_ptr_ = alloca((_size_ + _mask_) ?: 1); \
_ptr_ = alloca_safe(_size_ + _mask_); \
(void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_); \
})
@ -165,7 +160,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
void *_new_; \
size_t _xsize_ = (size); \
_new_ = alloca_align(_xsize_, (align)); \
(void*)memset(_new_, 0, _xsize_); \
memset(_new_, 0, _xsize_); \
})
#if HAS_FEATURE_MEMORY_SANITIZER
@ -193,3 +188,19 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0))
/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
#define strdupa_safe(s) \
({ \
const char *_t = (s); \
(char*) memdupa_suffix0(_t, strlen(_t)); \
})
#define strndupa_safe(s, n) \
({ \
const char *_t = (s); \
(char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
})
#include "memory-util.h"

View File

@ -2,6 +2,7 @@
#pragma once
#include <dirent.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@ -33,6 +34,9 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_FOREIGN,
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
/* The BPF hook implementing RestrictFileSystems= is not defined here.
* It's applied as late as possible in exec_child() so we don't block
* our own unit setup code. */
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
@ -123,6 +127,20 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) {
(x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX);
}
/* Special values for the special {blkio,io}.bfq.weight attribute */
#define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX
#define CGROUP_BFQ_WEIGHT_MIN UINT64_C(1)
#define CGROUP_BFQ_WEIGHT_MAX UINT64_C(1000)
#define CGROUP_BFQ_WEIGHT_DEFAULT UINT64_C(100)
/* Convert the normal io.weight value to io.bfq.weight */
static inline uint64_t BFQ_WEIGHT(uint64_t io_weight) {
return
io_weight <= CGROUP_WEIGHT_DEFAULT ?
CGROUP_BFQ_WEIGHT_DEFAULT - (CGROUP_WEIGHT_DEFAULT - io_weight) * (CGROUP_BFQ_WEIGHT_DEFAULT - CGROUP_BFQ_WEIGHT_MIN) / (CGROUP_WEIGHT_DEFAULT - CGROUP_WEIGHT_MIN) :
CGROUP_BFQ_WEIGHT_DEFAULT + (io_weight - CGROUP_WEIGHT_DEFAULT) * (CGROUP_BFQ_WEIGHT_MAX - CGROUP_BFQ_WEIGHT_DEFAULT) / (CGROUP_WEIGHT_MAX - CGROUP_WEIGHT_DEFAULT);
}
/* Special values for the blkio.weight attribute */
#define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX
#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10)
@ -236,6 +254,7 @@ int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
int cg_path_get_session(const char *path, char **session);
int cg_path_get_owner_uid(const char *path, uid_t *uid);
int cg_path_get_unit(const char *path, char **unit);
@ -311,3 +330,12 @@ typedef enum ManagedOOMPreference {
const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_;
ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_;
/* The structure to pass to name_to_handle_at() on cgroupfs2 */
typedef union {
struct file_handle file_handle;
uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
} cg_file_handle;
#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)

View File

@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "escape.h"
#include "extract-word.h"
#include "macro.h"
@ -397,7 +398,7 @@ int strv_env_replace_consume(char ***l, char *p) {
return -EINVAL;
}
name = strndupa(p, t - p);
name = strndupa_safe(p, t - p);
STRV_FOREACH(f, *l)
if (env_entry_has_name(*f, name)) {
@ -484,7 +485,7 @@ char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
t = strndupa(name, k);
t = strndupa_safe(name, k);
return getenv(t);
};
@ -791,15 +792,12 @@ int getenv_bool_secure(const char *p) {
#if 0 /* NM_IGNORED */
int set_unset_env(const char *name, const char *value, bool overwrite) {
int r;
assert(name);
if (value)
r = setenv(name, value, overwrite);
else
r = unsetenv(name);
if (r < 0)
return -errno;
return 0;
return RET_NERRNO(setenv(name, value, overwrite));
return RET_NERRNO(unsetenv(name));
}
int putenv_dup(const char *assignment, bool override) {
@ -809,12 +807,10 @@ int putenv_dup(const char *assignment, bool override) {
if (!e)
return -EINVAL;
n = strndupa(assignment, e - assignment);
n = strndupa_safe(assignment, e - assignment);
/* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
if (setenv(n, e + 1, override) < 0)
return -errno;
return 0;
return RET_NERRNO(setenv(n, e + 1, override));
}
int setenv_systemd_exec_pid(bool update_only) {

View File

@ -13,7 +13,7 @@ static inline void _reset_errno_(int *saved_errno) {
errno = *saved_errno;
}
#define PROTECT_ERRNO \
#define PROTECT_ERRNO \
_cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
#define UNPROTECT_ERRNO \
@ -31,6 +31,29 @@ static inline int negative_errno(void) {
return -errno;
}
static inline int RET_NERRNO(int ret) {
/* Helper to wrap system calls in to make them return negative errno errors. This brings system call
* error handling in sync with how we usually handle errors in our own code, i.e. with immediate
* returning of negative errno. Usage is like this:
*
*
* r = RET_NERRNO(unlink(t));
*
*
* or
*
*
* fd = RET_NERRNO(open("/etc/fstab", O_RDONLY|O_CLOEXEC));
*
*/
if (ret < 0)
return negative_errno();
return ret;
}
static inline const char *strerror_safe(int error) {
/* 'safe' here does NOT mean thread safety. */
return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */
@ -47,6 +70,13 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback);
}
/* For send()/recv() or read()/write(). */
static inline bool ERRNO_IS_TRANSIENT(int r) {
return IN_SET(abs(r),
EAGAIN,
EINTR);
}
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
* Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the
@ -77,10 +107,8 @@ static inline bool ERRNO_IS_DISCONNECT(int r) {
* the accept(2) man page. */
static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
return ERRNO_IS_DISCONNECT(r) ||
IN_SET(abs(r),
EAGAIN,
EINTR,
EOPNOTSUPP);
ERRNO_IS_TRANSIENT(r) ||
abs(r) == EOPNOTSUPP;
}
/* Resource exhaustion, could be our fault or general system trouble */

View File

@ -547,7 +547,7 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
return str_realloc(buf);
}
char* quote_command_line(char **argv) {
char* quote_command_line(char **argv, ShellEscapeFlags flags) {
_cleanup_free_ char *result = NULL;
assert(argv);
@ -556,7 +556,7 @@ char* quote_command_line(char **argv) {
STRV_FOREACH(a, argv) {
_cleanup_free_ char *t = NULL;
t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY);
t = shell_maybe_quote(*a, flags);
if (!t)
return NULL;
@ -564,6 +564,6 @@ char* quote_command_line(char **argv) {
return NULL;
}
return TAKE_PTR(result);
return str_realloc(TAKE_PTR(result));
}
#endif /* NM_IGNORED */

View File

@ -69,4 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
char* shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
char* quote_command_line(char **argv);
char* quote_command_line(char **argv, ShellEscapeFlags flags);

View File

@ -9,20 +9,29 @@
#include <sys/types.h>
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "string-util.h"
char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) {
char *hw_addr_to_string_full(
const struct hw_addr_data *addr,
HardwareAddressToStringFlags flags,
char buffer[static HW_ADDR_TO_STRING_MAX]) {
assert(addr);
assert(buffer);
assert(addr->length <= HW_ADDR_MAX_SIZE);
for (size_t i = 0; i < addr->length; i++) {
sprintf(&buffer[3*i], "%02"PRIx8, addr->bytes[i]);
if (i < addr->length - 1)
buffer[3*i + 2] = ':';
for (size_t i = 0, j = 0; i < addr->length; i++) {
buffer[j++] = hexchar(addr->bytes[i] >> 4);
buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
buffer[j++] = ':';
}
buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
addr->length * 2 :
addr->length * 3 - 1] = '\0';
return buffer;
}
@ -39,7 +48,7 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b)
return memcmp(a->bytes, b->bytes, a->length);
}
static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p);
assert(state);
@ -48,6 +57,7 @@ static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *stat
}
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
assert(addr);
@ -93,75 +103,163 @@ static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *sta
}
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
int ether_addr_from_string(const char *s, struct ether_addr *ret) {
size_t pos = 0, n, field;
char sep = '\0';
const char *hex = HEXDIGITS, *hexoff;
size_t x;
bool touched;
static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
const char *hex = HEXDIGITS, *p;
uint16_t data = 0;
bool cont;
#define parse_fields(v) \
for (field = 0; field < ELEMENTSOF(v); field++) { \
touched = false; \
for (n = 0; n < (2 * sizeof(v[0])); n++) { \
if (s[pos] == '\0') \
break; \
hexoff = strchr(hex, s[pos]); \
if (!hexoff) \
break; \
assert(hexoff >= hex); \
x = hexoff - hex; \
if (x >= 16) \
x -= 6; /* A-F */ \
assert(x < 16); \
touched = true; \
v[field] <<= 4; \
v[field] += x; \
pos++; \
} \
if (!touched) \
return -EINVAL; \
if (field < (ELEMENTSOF(v)-1)) { \
if (s[pos] != sep) \
return -EINVAL; \
else \
pos++; \
} \
assert(s);
assert(*s);
assert(IN_SET(len, 1, 2));
assert(buf);
p = *s;
for (size_t i = 0; i < len * 2; i++) {
const char *hexoff;
size_t x;
if (*p == '\0' || *p == sep) {
if (i == 0)
return -EINVAL;
break;
}
hexoff = strchr(hex, *p);
if (!hexoff)
return -EINVAL;
assert(hexoff >= hex);
x = hexoff - hex;
if (x >= 16)
x -= 6; /* A-F */
assert(x < 16);
data <<= 4;
data += x;
p++;
}
if (*p != '\0' && *p != sep)
return -EINVAL;
switch (len) {
case 1:
buf[0] = data;
break;
case 2:
buf[0] = (data & 0xff00) >> 8;
buf[1] = data & 0xff;
break;
default:
assert_not_reached();
}
cont = *p == sep;
*s = p + cont;
return cont;
}
int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
size_t field_size, max_len, len = 0;
uint8_t bytes[HW_ADDR_MAX_SIZE];
char sep;
int r;
assert(s);
assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
assert(ret);
/* This accepts the following formats:
*
* Dot separated 2 bytes format: xxyy.zzaa.bbcc
* Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc
* Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc
*
* Moreover, if expected_len == 0, 4, or 16, this also accepts:
*
* IPv4 format: used by IPv4 tunnel, e.g. ipgre
* IPv6 format: used by IPv6 tunnel, e.g. ip6gre
*
* The expected_len argument controls the length of acceptable addresses:
*
* 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN).
* SIZE_MAX: accepts arbitrary length, but at least one separator must be included.
* Otherwise: accepts addresses with matching length.
*/
if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
union in_addr_union a;
int family;
if (expected_len == 0)
r = in_addr_from_string_auto(s, &family, &a);
else {
family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
r = in_addr_from_string(family, s, &a);
}
if (r >= 0) {
ret->length = FAMILY_ADDRESS_SIZE(family);
memcpy(ret->bytes, a.bytes, ret->length);
return 0;
}
}
max_len =
expected_len == 0 ? INFINIBAND_ALEN :
expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
sep = s[strspn(s, HEXDIGITS)];
if (sep == '.')
field_size = 2;
else if (IN_SET(sep, ':', '-'))
field_size = 1;
else
return -EINVAL;
if (max_len % field_size != 0)
return -EINVAL;
for (size_t i = 0; i < max_len / field_size; i++) {
r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
if (r < 0)
return r;
if (r == 0) {
len = (i + 1) * field_size;
break;
}
}
if (len == 0)
return -EINVAL;
if (expected_len == 0) {
if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
return -EINVAL;
} else if (expected_len != SIZE_MAX) {
if (len != expected_len)
return -EINVAL;
}
ret->length = len;
memcpy(ret->bytes, bytes, ret->length);
return 0;
}
int parse_ether_addr(const char *s, struct ether_addr *ret) {
struct hw_addr_data a;
int r;
assert(s);
assert(ret);
s += strspn(s, WHITESPACE);
sep = s[strspn(s, hex)];
if (sep == '.') {
uint16_t shorts[3] = { 0 };
parse_fields(shorts);
if (s[pos] != '\0')
return -EINVAL;
for (n = 0; n < ELEMENTSOF(shorts); n++) {
ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
}
} else if (IN_SET(sep, ':', '-')) {
struct ether_addr out = ETHER_ADDR_NULL;
parse_fields(out.ether_addr_octet);
if (s[pos] != '\0')
return -EINVAL;
for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
ret->ether_addr_octet[n] = out.ether_addr_octet[n];
} else
return -EINVAL;
r = parse_hw_addr_full(s, ETH_ALEN, &a);
if (r < 0)
return r;
*ret = a.ether;
return 0;
}

View File

@ -6,6 +6,9 @@
#include <stdbool.h>
#include "hash-funcs.h"
#include "in-addr-util.h"
#include "macro.h"
#include "memory-util.h"
/* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h
* defines a macro of the same name with a much lower size. */
@ -16,29 +19,51 @@ struct hw_addr_data {
union {
struct ether_addr ether;
uint8_t infiniband[INFINIBAND_ALEN];
struct in_addr in;
struct in6_addr in6;
uint8_t bytes[HW_ADDR_MAX_SIZE];
};
};
int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret);
static inline int parse_hw_addr(const char *s, struct hw_addr_data *ret) {
return parse_hw_addr_full(s, 0, ret);
}
int parse_ether_addr(const char *s, struct ether_addr *ret);
typedef enum HardwareAddressToStringFlags {
HW_ADDR_TO_STRING_NO_COLON = 1 << 0,
} HardwareAddressToStringFlags;
#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
char *hw_addr_to_string_full(
const struct hw_addr_data *addr,
HardwareAddressToStringFlags flags,
char buffer[static HW_ADDR_TO_STRING_MAX]);
static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) {
return hw_addr_to_string_full(addr, 0, buffer);
}
/* Note: the lifetime of the compound literal is the immediately surrounding block,
* see C11 §6.5.2.5, and
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){})
#define HW_ADDR_TO_STR_FULL(hw_addr, flags) hw_addr_to_string_full((hw_addr), flags, (char[HW_ADDR_TO_STRING_MAX]){})
#define HW_ADDR_TO_STR(hw_addr) HW_ADDR_TO_STR_FULL(hw_addr, 0)
#define HW_ADDR_NULL ((const struct hw_addr_data){})
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
return hw_addr_compare(a, b) == 0;
}
static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
return hw_addr_equal(addr, &HW_ADDR_NULL);
assert(addr);
return addr->length == 0 || memeqzero(addr->bytes, addr->length);
}
extern const struct hash_ops hw_addr_hash_ops;
extern const struct hash_ops hw_addr_hash_ops_free;
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
@ -60,6 +85,29 @@ static inline bool ether_addr_is_null(const struct ether_addr *addr) {
return ether_addr_equal(addr, &ETHER_ADDR_NULL);
}
int ether_addr_from_string(const char *s, struct ether_addr *ret);
static inline bool ether_addr_is_broadcast(const struct ether_addr *addr) {
assert(addr);
return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN);
}
static inline bool ether_addr_is_multicast(const struct ether_addr *addr) {
assert(addr);
return FLAGS_SET(addr->ether_addr_octet[0], 0x01);
}
static inline bool ether_addr_is_unicast(const struct ether_addr *addr) {
return !ether_addr_is_multicast(addr);
}
static inline bool ether_addr_is_local(const struct ether_addr *addr) {
/* Determine if the Ethernet address is locally-assigned one (IEEE 802) */
assert(addr);
return FLAGS_SET(addr->ether_addr_octet[0], 0x02);
}
static inline bool ether_addr_is_global(const struct ether_addr *addr) {
return !ether_addr_is_local(addr);
}
extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;

View File

@ -154,10 +154,7 @@ int fd_nonblock(int fd, bool nonblock) {
if (nflags == flags)
return 0;
if (fcntl(fd, F_SETFL, nflags) < 0)
return -errno;
return 0;
return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
}
int fd_cloexec(int fd, bool cloexec) {
@ -173,10 +170,7 @@ int fd_cloexec(int fd, bool cloexec) {
if (nflags == flags)
return 0;
if (fcntl(fd, F_SETFD, nflags) < 0)
return -errno;
return 0;
return RET_NERRNO(fcntl(fd, F_SETFD, nflags));
}
#if 0 /* NM_IGNORED */
@ -190,7 +184,7 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
return false;
}
static int get_max_fd(void) {
int get_max_fd(void) {
struct rlimit rl;
rlim_t m;
@ -211,104 +205,17 @@ static int get_max_fd(void) {
return (int) (m - 1);
}
int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
static bool have_close_range = true; /* Assume we live in the future */
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
static int close_all_fds_frugal(const int except[], size_t n_except) {
int max_fd, r = 0;
assert(n_except == 0 || except);
if (have_close_range) {
/* In the best case we have close_range() to close all fds between a start and an end fd,
* which we can use on the "inverted" exception array, i.e. all intervals between all
* adjacent pairs from the sorted exception array. This changes loop complexity from O(n)
* where n is number of open fds to O(mlog(m)) where m is the number of fds to keep
* open. Given that we assume n m that's preferable to us. */
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
* but when we assume we are called from signal handler context, then use this simpler call
* instead. */
if (n_except == 0) {
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
return 0;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
else
return -errno;
} else {
typesafe_qsort(except, n_except, cmp_int);
for (size_t i = 0; i < n_except; i++) {
int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */
int end = MAX(except[i], 2);
assert(end >= start);
if (end - start <= 1)
continue;
/* Close everything between the start and end fds (both of which shall stay open) */
if (close_range(start + 1, end - 1, 0) < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
else
return -errno;
goto opendir_fallback;
}
}
/* The loop succeeded. Let's now close everything beyond the end */
if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */
return 0;
int start = MAX(except[n_except-1], 2);
if (close_range(start + 1, -1, 0) >= 0)
return 0;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
have_close_range = false;
else
return -errno;
}
}
/* Fallback for when close_range() is not supported */
opendir_fallback:
d = allow_alloc ? opendir("/proc/self/fd") : NULL;
if (d) {
struct dirent *de;
FOREACH_DIRENT(de, d, return -errno) {
int fd = -1, q;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
r = q;
}
return r;
}
/* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by
* brute-forcing through the file descriptor table. */
int max_fd = get_max_fd();
max_fd = get_max_fd();
if (max_fd < 0)
return max_fd;
@ -316,7 +223,7 @@ int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
* spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
"/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
"Refusing to loop over %d potential fds.",
max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
@ -333,6 +240,178 @@ int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
return r;
}
static bool have_close_range = true; /* Assume we live in the future */
static int close_all_fds_special_case(const int except[], size_t n_except) {
assert(n_except == 0 || except);
/* Handles a few common special cases separately, since they are common and can be optimized really
* nicely, since we won't need sorting for them. Returns > 0 if the special casing worked, 0
* otherwise. */
if (!have_close_range)
return 0;
switch (n_except) {
case 0:
/* Close everything. Yay! */
if (close_range(3, -1, 0) >= 0)
return 1;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
have_close_range = false;
return 0;
}
return -errno;
case 1:
/* Close all but exactly one, then we don't need no sorting. This is a pretty common
* case, hence let's handle it specially. */
if ((except[0] <= 3 || close_range(3, except[0]-1, 0) >= 0) &&
(except[0] >= INT_MAX || close_range(MAX(3, except[0]+1), -1, 0) >= 0))
return 1;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
have_close_range = false;
return 0;
}
return -errno;
default:
return 0;
}
}
int close_all_fds_without_malloc(const int except[], size_t n_except) {
int r;
assert(n_except == 0 || except);
r = close_all_fds_special_case(except, n_except);
if (r < 0)
return r;
if (r > 0) /* special case worked! */
return 0;
return close_all_fds_frugal(except, n_except);
}
int close_all_fds(const int except[], size_t n_except) {
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
assert(n_except == 0 || except);
r = close_all_fds_special_case(except, n_except);
if (r < 0)
return r;
if (r > 0) /* special case worked! */
return 0;
if (have_close_range) {
_cleanup_free_ int *sorted_malloc = NULL;
size_t n_sorted;
int *sorted;
/* In the best case we have close_range() to close all fds between a start and an end fd,
* which we can use on the "inverted" exception array, i.e. all intervals between all
* adjacent pairs from the sorted exception array. This changes loop complexity from O(n)
* where n is number of open fds to O(mlog(m)) where m is the number of fds to keep
* open. Given that we assume n m that's preferable to us. */
assert(n_except < SIZE_MAX);
n_sorted = n_except + 1;
if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
sorted = sorted_malloc = new(int, n_sorted);
else
sorted = newa(int, n_sorted);
if (sorted) {
memcpy(sorted, except, n_except * sizeof(int));
/* Let's add fd 2 to the list of fds, to simplify the loop below, as this
* allows us to cover the head of the array the same way as the body */
sorted[n_sorted-1] = 2;
typesafe_qsort(sorted, n_sorted, cmp_int);
for (size_t i = 0; i < n_sorted-1; i++) {
int start, end;
start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
end = MAX(sorted[i+1], 2);
assert(end >= start);
if (end - start <= 1)
continue;
/* Close everything between the start and end fds (both of which shall stay open) */
if (close_range(start + 1, end - 1, 0) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
break;
}
}
if (have_close_range) {
/* The loop succeeded. Let's now close everything beyond the end */
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
return 0;
if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
return 0;
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
}
}
/* Fallback on OOM or if close_range() is not supported */
}
d = opendir("/proc/self/fd");
if (!d)
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;
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
r = q;
}
return r;
}
int same_fd(int a, int b) {
struct stat sta, stb;
pid_t pid;
@ -652,7 +731,7 @@ finish:
}
int fd_reopen(int fd, int flags) {
int new_fd;
int new_fd, r;
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
* turn O_RDWR fds into O_RDONLY fds.
@ -661,15 +740,28 @@ int fd_reopen(int fd, int flags) {
*
* This implicitly resets the file read index to 0. */
if (FLAGS_SET(flags, O_DIRECTORY)) {
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
new_fd = openat(fd, ".", flags);
if (new_fd < 0)
return -errno;
return new_fd;
}
new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;
if (proc_mounted() == 0)
r = proc_mounted();
if (r == 0)
return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
return -ENOENT;
return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is
* not valid, otherwise let's propagate the original
* error */
}
return new_fd;
@ -710,9 +802,6 @@ int btrfs_defrag_fd(int fd) {
if (r < 0)
return r;
if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
return -errno;
return 0;
return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL));
}
#endif /* NM_IGNORED */

View File

@ -57,10 +57,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec);
int close_all_fds_full(int except[], size_t n_except, bool allow_alloc);
static inline int close_all_fds(int except[], size_t n_except) {
return close_all_fds_full(except, n_except, true);
}
int get_max_fd(void);
int close_all_fds(const int except[], size_t n_except);
int close_all_fds_without_malloc(const int except[], size_t n_except);
int same_fd(int a, int b);
@ -91,9 +91,10 @@ static inline int make_null_stdio(void) {
/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
#define TAKE_FD(fd) \
({ \
int _fd_ = (fd); \
(fd) = -1; \
_fd_; \
int *_fd_ = &(fd); \
int _ret_ = *_fd_; \
*_fd_ = -1; \
_ret_; \
})
/* Like free_and_replace(), but for file descriptors */

View File

@ -169,7 +169,7 @@ int write_string_stream_ts(
* it won't be equal to the new value. */
if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 &&
streq_skip_trailing_chars(line, t, NEWLINE)) {
log_debug("No change in value '%s', supressing write", line);
log_debug("No change in value '%s', suppressing write", line);
return 0;
}
@ -551,12 +551,25 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
return !truncated;
}
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
int read_virtual_file_at(
int dir_fd,
const char *filename,
size_t max_size,
char **ret_contents,
size_t *ret_size) {
_cleanup_close_ int fd = -1;
assert(filename);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (!filename) {
if (dir_fd == AT_FDCWD)
return -EBADF;
return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
}
fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (fd < 0)
return -errno;
@ -934,6 +947,9 @@ DIR *xopendirat(int fd, const char *name, int flags) {
assert(!(flags & O_CREAT));
if (fd == AT_FDCWD && flags == 0)
return opendir(name);
nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
if (nfd < 0)
return NULL;

View File

@ -69,7 +69,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
}
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
}
static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
}

View File

@ -26,11 +26,15 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t));
# error Unknown time_t size
#endif
#if defined __x86_64__ && defined __ILP32__
#if 0 /* NM_IGNORED */
#if SIZEOF_TIMEX_MEMBER == 8
# define PRI_TIMEX PRIi64
#else
#elif SIZEOF_TIMEX_MEMBER == 4
# define PRI_TIMEX "li"
#else
# error Unknown timex member size
#endif
#endif /* NM_IGNORED */
#if SIZEOF_RLIM_T == 8
# define RLIM_FMT "%" PRIu64

View File

@ -14,6 +14,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "log.h"
#include "macro.h"
#include "missing_fcntl.h"
@ -31,18 +32,13 @@
#include "strv.h"
#include "time-util.h"
#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
int unlink_noerrno(const char *path) {
PROTECT_ERRNO;
int r;
r = unlink(path);
if (r < 0)
return -errno;
return 0;
return RET_NERRNO(unlink(path));
}
#if 0 /* NM_IGNORED */
@ -59,7 +55,7 @@ int rmdir_parents(const char *path, const char *stop) {
if (!path_is_safe(stop))
return -EINVAL;
p = strdupa(path);
p = strdupa_safe(path);
for (;;) {
char *slash = NULL;
@ -99,8 +95,8 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
* want though not atomic (i.e. for a short period both the new and the old filename will exist). */
if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) {
if (unlinkat(olddirfd, oldpath, 0) < 0) {
r = -errno; /* Backup errno before the following unlinkat() alters it */
r = RET_NERRNO(unlinkat(olddirfd, oldpath, 0));
if (r < 0) {
(void) unlinkat(newdirfd, newpath, 0);
return r;
}
@ -119,10 +115,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
if (errno != ENOENT)
return -errno;
if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0)
return -errno;
return 0;
return RET_NERRNO(renameat(olddirfd, oldpath, newdirfd, newpath));
}
#endif /* NM_IGNORED */
@ -290,14 +283,9 @@ int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t
}
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
_cleanup_umask_ mode_t u = umask(0777);
u = umask(0777);
r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
umask(u);
return r;
return RET_NERRNO(fchmod(fd, m & (~u)));
}
int fchmod_opath(int fd, mode_t m) {
@ -550,7 +538,6 @@ int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
int get_files_in_directory(const char *path, char ***list) {
_cleanup_strv_free_ char **l = NULL;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
size_t n = 0;
assert(path);
@ -830,7 +817,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
int open_parent(const char *path, int flags, mode_t mode) {
_cleanup_free_ char *parent = NULL;
int fd, r;
int r;
r = path_extract_directory(path, &parent);
if (r < 0)
@ -844,11 +831,7 @@ int open_parent(const char *path, int flags, mode_t mode) {
else if (!FLAGS_SET(flags, O_TMPFILE))
flags |= O_DIRECTORY|O_RDONLY;
fd = open(parent, flags, mode);
if (fd < 0)
return -errno;
return fd;
return RET_NERRNO(open(parent, flags, mode));
}
#endif /* NM_IGNORED */
@ -962,3 +945,152 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
return -EINTR;
}
int parse_cifs_service(
const char *s,
char **ret_host,
char **ret_service,
char **ret_path) {
_cleanup_free_ char *h = NULL, *ss = NULL, *x = NULL;
const char *p, *e, *d;
char delimiter;
/* Parses a CIFS service in form of //host/service/path… and splitting it in three parts. The last
* part is optional, in which case NULL is returned there. To maximize compatibility syntax with
* backslashes instead of slashes is accepted too. */
if (!s)
return -EINVAL;
p = startswith(s, "//");
if (!p) {
p = startswith(s, "\\\\");
if (!p)
return -EINVAL;
}
delimiter = s[0];
e = strchr(p, delimiter);
if (!e)
return -EINVAL;
h = strndup(p, e - p);
if (!h)
return -ENOMEM;
if (!hostname_is_valid(h, 0))
return -EINVAL;
e++;
d = strchrnul(e, delimiter);
ss = strndup(e, d - e);
if (!ss)
return -ENOMEM;
if (!filename_is_valid(ss))
return -EINVAL;
if (!isempty(d)) {
x = strdup(skip_leading_chars(d, CHAR_TO_STR(delimiter)));
if (!x)
return -EINVAL;
/* Make sure to convert Windows-style "\" → Unix-style / */
for (char *i = x; *i; i++)
if (*i == delimiter)
*i = '/';
if (!path_is_valid(x))
return -EINVAL;
path_simplify(x);
if (!path_is_normalized(x))
return -EINVAL;
}
if (ret_host)
*ret_host = TAKE_PTR(h);
if (ret_service)
*ret_service = TAKE_PTR(ss);
if (ret_path)
*ret_path = TAKE_PTR(x);
return 0;
}
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
_cleanup_close_ int fd = -1, parent_fd = -1;
_cleanup_free_ char *fname = NULL;
bool made;
int r;
/* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can
* do. Guarantees that the returned fd refers to a directory. If O_EXCL is specified will fail if the
* dir already exists. Otherwise will open an existing dir, but only if it is one. */
if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
return -EINVAL;
if ((flags & O_ACCMODE) != O_RDONLY)
return -EINVAL;
/* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
* flags actually make sense to specify: O_CLOEXEC, O_EXCL, O_NOATIME, O_PATH */
if (isempty(path))
return -EINVAL;
if (!filename_is_valid(path)) {
_cleanup_free_ char *parent = NULL;
/* If this is not a valid filename, it's a path. Let's open the parent directory then, so
* that we can pin it, and operate below it. */
r = path_extract_directory(path, &parent);
if (r < 0)
return r;
r = path_extract_filename(path, &fname);
if (r < 0)
return r;
parent_fd = openat(dirfd, parent, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (parent_fd < 0)
return -errno;
dirfd = parent_fd;
path = fname;
}
r = RET_NERRNO(mkdirat(dirfd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(flags, O_EXCL))
return -EEXIST;
made = false;
} else if (r < 0)
return r;
else
made = true;
fd = RET_NERRNO(openat(dirfd, path, (flags & ~O_EXCL)|O_DIRECTORY|O_NOFOLLOW));
if (fd < 0) {
if (fd == -ENOENT) /* We got ENOENT? then someone else immediately removed it after we
* created it. In that case let's return immediately without unlinking
* anything, because there simply isn't anything to unlink anymore. */
return -ENOENT;
if (fd == -ELOOP) /* is a symlink? exists already → created by someone else, don't unlink */
return -EEXIST;
if (fd == -ENOTDIR) /* not a directory? exists already → created by someone else, don't unlink */
return -EEXIST;
if (made)
(void) unlinkat(dirfd, path, AT_REMOVEDIR);
return fd;
}
return TAKE_FD(fd);
}

View File

@ -47,7 +47,7 @@ int fd_warn_permissions(const char *path, int fd);
int stat_warn_permissions(const char *path, const struct stat *st);
#define laccess(path, mode) \
(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) < 0 ? -errno : 0)
RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW))
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
int touch(const char *path);
@ -106,3 +106,7 @@ static inline int conservative_rename(const char *oldpath, const char *newpath)
}
int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);

View File

@ -49,8 +49,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) {
assert_se(uname(&u) >= 0);
s = u.nodename;
if (isempty(s) ||
(!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) ||
if (isempty(s) || streq(s, "(none)") ||
(!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
(FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))

View File

@ -9,10 +9,9 @@
#include "strv.h"
typedef enum GetHostnameFlags {
GET_HOSTNAME_ALLOW_NONE = 1 << 0, /* accepts "(none)". */
GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 1, /* accepts "localhost" or friends. */
GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */
GET_HOSTNAME_SHORT = 1 << 3, /* kills the FQDN part if present. */
GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 0, /* accepts "localhost" or friends. */
GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */
GET_HOSTNAME_SHORT = 1 << 2, /* kills the FQDN part if present. */
} GetHostnameFlags;
int gethostname_full(GetHostnameFlags flags, char **ret);

View File

@ -629,65 +629,120 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
return 0;
}
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen) {
struct in_addr mask;
assert(addr);
if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
return -EINVAL;
addr->s_addr &= mask.s_addr;
return 0;
}
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else if (prefixlen > 0) {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
} else {
assert(prefixlen == 0);
mask = 0;
}
addr->s6_addr[i] &= mask;
}
return 0;
}
#if 0 /* NM_IGNORED */
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
assert(addr);
if (family == AF_INET) {
struct in_addr mask;
if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
return -EINVAL;
addr->in.s_addr &= mask.s_addr;
return 0;
switch (family) {
case AF_INET:
return in4_addr_mask(&addr->in, prefixlen);
case AF_INET6:
return in6_addr_mask(&addr->in6, prefixlen);
default:
return -EAFNOSUPPORT;
}
if (family == AF_INET6) {
unsigned i;
for (i = 0; i < 16; i++) {
uint8_t mask;
if (prefixlen >= 8) {
mask = 0xFF;
prefixlen -= 8;
} else {
mask = 0xFF << (8 - prefixlen);
prefixlen = 0;
}
addr->in6.s6_addr[i] &= mask;
}
return 0;
}
return -EAFNOSUPPORT;
}
int in_addr_prefix_covers(int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
int in4_addr_prefix_covers(
const struct in_addr *prefix,
unsigned char prefixlen,
const struct in_addr *address) {
union in_addr_union masked_prefix, masked_address;
struct in_addr masked_prefix, masked_address;
int r;
assert(prefix);
assert(address);
masked_prefix = *prefix;
r = in_addr_mask(family, &masked_prefix, prefixlen);
r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
return r;
masked_address = *address;
r = in_addr_mask(family, &masked_address, prefixlen);
r = in4_addr_mask(&masked_address, prefixlen);
if (r < 0)
return r;
return in_addr_equal(family, &masked_prefix, &masked_address);
return in4_addr_equal(&masked_prefix, &masked_address);
}
int in6_addr_prefix_covers(
const struct in6_addr *prefix,
unsigned char prefixlen,
const struct in6_addr *address) {
struct in6_addr masked_prefix, masked_address;
int r;
assert(prefix);
assert(address);
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
return r;
masked_address = *address;
r = in6_addr_mask(&masked_address, prefixlen);
if (r < 0)
return r;
return in6_addr_equal(&masked_prefix, &masked_address);
}
int in_addr_prefix_covers(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
case AF_INET6:
return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
default:
return -EAFNOSUPPORT;
}
}
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
@ -855,4 +910,10 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
}
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,
in6_addr_hash_func,
in6_addr_compare_func,
free);
#endif /* NM_IGNORED */

View File

@ -89,7 +89,11 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@ -110,6 +114,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
return family == AF_INET6 ? 16 : 4;
}
#define FAMILY_ADDRESS_SIZE_SAFE(f) \
({ \
int _f = (f); \
_f == AF_INET ? sizeof(struct in_addr) : \
_f == AF_INET6 ? sizeof(struct in6_addr) : 0; \
})
/* Workaround for clang, explicitly specify the maximum-size element here.
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
@ -119,6 +130,7 @@ 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 in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
#define IPV4_ADDRESS_FMT_VAL(address) \

View File

@ -4,14 +4,26 @@
#include "fd-util.h"
#include "inotify-util.h"
#include "stat-util.h"
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd;
int wd, r;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
if (wd < 0)
return -errno;
if (wd < 0) {
if (errno != ENOENT)
return -errno;
/* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */
r = proc_mounted();
if (r == 0)
return -ENOSYS;
if (r > 0)
return -EBADF;
return -ENOENT; /* OK, no clue, let's propagate the original error */
}
return wd;
}

View File

@ -134,7 +134,7 @@
} while (false)
#define LIST_JUST_US(name,item) \
(!(item)->name##_prev && !(item)->name##_next) \
(!(item)->name##_prev && !(item)->name##_next)
#define LIST_FOREACH(name,i,head) \
for ((i) = (head); (i); (i) = (i)->name##_next)
@ -181,3 +181,12 @@
} \
(b) = NULL; \
} while (false)
#define LIST_POP(name, a) \
({ \
typeof(a)* _a = &(a); \
typeof(a) _p = *_a; \
if (_p) \
LIST_REMOVE(name, *_a, _p); \
_p; \
})

View File

@ -27,10 +27,14 @@ typedef enum LogTarget{
_LOG_TARGET_INVALID = -EINVAL,
} LogTarget;
/* Note to readers: << and >> have lower precedence than & and | */
/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
* used a regular log level. */
#define LOG_NULL (LOG_EMERG - 1)
/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
#define SYNTHETIC_ERRNO(num) (1 << 30 | (num))
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255)
#define ERRNO_VALUE(val) (abs(val) & ~(1 << 30))
/* The callback function to be invoked when syntax warnings are seen
* in the unit files. */
@ -90,6 +94,7 @@ int log_open(void);
void log_close(void);
void log_forget_fds(void);
void log_parse_environment_variables(void);
void log_parse_environment(void);
#if 0 /* NM_IGNORED */

View File

@ -25,7 +25,7 @@
#define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
@ -154,24 +154,6 @@
#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
static inline size_t ALIGN_TO(size_t l, size_t ali) {
/* Check that alignment is exponent of 2 */
#if SIZE_MAX == UINT_MAX
assert(__builtin_popcount(ali) == 1);
#elif SIZE_MAX == ULONG_MAX
assert(__builtin_popcountl(ali) == 1);
#elif SIZE_MAX == ULLONG_MAX
assert(__builtin_popcountll(ali) == 1);
#else
#error "Unexpected size_t"
#endif
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
return ((l + ali - 1) & ~(ali - 1));
}
#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
@ -355,13 +337,6 @@ static inline int __coverity_check_and_return__(int condition) {
ans; \
})
#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)
#define SWAP_TWO(x, y) do { \
typeof(x) _t = (x); \
(x) = (y); \

View File

@ -20,6 +20,7 @@
#include <asm/sgidefs.h>
#endif
#include "macro.h"
#include "missing_keyctl.h"
#include "missing_stat.h"
#include "missing_syscall_def.h"
@ -79,7 +80,8 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
/* ======================================================================= */
#if !HAVE_GETRANDOM
static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
/* glibc says getrandom() returns ssize_t */
static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
# ifdef __NR_getrandom
return syscall(__NR_getrandom, buffer, count, flags);
# else
@ -466,8 +468,52 @@ struct mount_attr {
struct mount_attr;
#endif
#ifndef MOUNT_ATTR_RDONLY
#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */
#endif
#ifndef MOUNT_ATTR_NOSUID
#define MOUNT_ATTR_NOSUID 0x00000002 /* Ignore suid and sgid bits */
#endif
#ifndef MOUNT_ATTR_NODEV
#define MOUNT_ATTR_NODEV 0x00000004 /* Disallow access to device special files */
#endif
#ifndef MOUNT_ATTR_NOEXEC
#define MOUNT_ATTR_NOEXEC 0x00000008 /* Disallow program execution */
#endif
#ifndef MOUNT_ATTR__ATIME
#define MOUNT_ATTR__ATIME 0x00000070 /* Setting on how atime should be updated */
#endif
#ifndef MOUNT_ATTR_RELATIME
#define MOUNT_ATTR_RELATIME 0x00000000 /* - Update atime relative to mtime/ctime. */
#endif
#ifndef MOUNT_ATTR_NOATIME
#define MOUNT_ATTR_NOATIME 0x00000010 /* - Do not update access times. */
#endif
#ifndef MOUNT_ATTR_STRICTATIME
#define MOUNT_ATTR_STRICTATIME 0x00000020 /* - Always perform atime updates */
#endif
#ifndef MOUNT_ATTR_NODIRATIME
#define MOUNT_ATTR_NODIRATIME 0x00000080 /* Do not update directory access times */
#endif
#ifndef MOUNT_ATTR_IDMAP
#define MOUNT_ATTR_IDMAP 0x00100000
#define MOUNT_ATTR_IDMAP 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
#endif
#ifndef MOUNT_ATTR_NOSYMFOLLOW
#define MOUNT_ATTR_NOSYMFOLLOW 0x00200000 /* Do not follow symlinks */
#endif
#ifndef MOUNT_ATTR_SIZE_VER0
#define MOUNT_ATTR_SIZE_VER0 32 /* sizeof first published struct */
#endif
#ifndef AT_RECURSIVE
@ -545,4 +591,20 @@ static inline int missing_move_mount(
# define move_mount missing_move_mount
#endif
/* ======================================================================= */
#if !HAVE_GETDENTS64
static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
# if defined __NR_getdents64 && __NR_getdents64 >= 0
return syscall(__NR_getdents64, fd, buffer, length);
# else
errno = ENOSYS;
return -1;
# endif
}
# define getdents64 missing_getdents64
#endif
#endif /* NM_IGNORED */

View File

@ -651,7 +651,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
uint16_t l;
int r;
r = safe_atou16(s, &l);
r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l);
if (r < 0)
return r;
@ -711,7 +711,7 @@ int parse_dev(const char *s, dev_t *ret) {
if (s[n] != ':')
return -EINVAL;
major = strndupa(s, n);
major = strndupa_safe(s, n);
r = safe_atou(major, &x);
if (r < 0)
return r;
@ -772,7 +772,7 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
if (!d)
return -EINVAL;
i_str = strndupa(s, d - s);
i_str = strndupa_safe(s, d - s);
f_str = d + 1;
r = safe_atolu_full(i_str, 10, &i);

View File

@ -1246,8 +1246,6 @@ char *file_in_same_dir(const char *path, const char *filename) {
}
bool hidden_or_backup_file(const char *filename) {
const char *p;
assert(filename);
if (filename[0] == '.' ||
@ -1257,24 +1255,25 @@ bool hidden_or_backup_file(const char *filename) {
endswith(filename, "~"))
return true;
p = strrchr(filename, '.');
if (!p)
const char *dot = strrchr(filename, '.');
if (!dot)
return false;
/* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
* with always new suffixes and that everybody else should just adjust to that, then it really should be on
* them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
* one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
* string. Specifically: there's now:
/* Please, let's not add more entries to the list below. If external projects think it's a good idea
* to come up with always new suffixes and that everybody else should just adjust to that, then it
* really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask
* those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups,
* possibly augmented with an additional string. Specifically: there's now:
*
* The generic suffixes "~" and ".bak" for backup files
* The generic prefix "." for hidden files
*
* Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
* or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
* Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old",
* ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new",
* ".foopkg.old" or ".foopkg~" instead.
*/
return STR_IN_SET(p + 1,
return STR_IN_SET(dot + 1,
"rpmnew",
"rpmsave",
"rpmorig",
@ -1296,15 +1295,16 @@ bool hidden_or_backup_file(const char *filename) {
bool is_device_path(const char *path) {
/* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */
/* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
* /dev. */
return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
}
bool valid_device_node_path(const char *path) {
/* Some superficial checks whether the specified path is a valid device node path, all without looking at the
* actual device node. */
/* Some superficial checks whether the specified path is a valid device node path, all without
* looking at the actual device node. */
if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
return false;
@ -1318,8 +1318,8 @@ bool valid_device_node_path(const char *path) {
bool valid_device_allow_pattern(const char *path) {
assert(path);
/* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
* accept it */
/* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by
* DeviceAllow= and DeviceDeny=. */
if (STARTSWITH_SET(path, "block-", "char-"))
return true;
@ -1412,8 +1412,8 @@ bool dot_or_dot_dot(const char *path) {
#if 0 /* NM_IGNORED */
bool empty_or_root(const char *path) {
/* For operations relative to some root directory, returns true if the specified root directory is redundant,
* i.e. either / or NULL or the empty string or any equivalent. */
/* For operations relative to some root directory, returns true if the specified root directory is
* redundant, i.e. either / or NULL or the empty string or any equivalent. */
if (isempty(path))
return true;

View File

@ -187,13 +187,13 @@ static int get_process_cmdline_nulstr(
return r;
}
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
_cleanup_free_ char *t = NULL;
size_t k;
char *ans;
assert(line);
assert(pid >= 0);
assert(ret);
/* Retrieve and format a commandline. See above for discussion of retrieval options.
*
@ -220,25 +220,23 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
_cleanup_strv_free_ char **args = NULL;
char **p;
args = strv_parse_nulstr(t, k);
if (!args)
return -ENOMEM;
for (size_t i = 0; args[i]; i++) {
char *e;
/* Drop trailing empty strings. See issue #21186. */
STRV_FOREACH_BACKWARDS(p, args) {
if (!isempty(*p))
break;
e = shell_maybe_quote(args[i], shflags);
if (!e)
return -ENOMEM;
free_and_replace(args[i], e);
*p = mfree(*p);
}
ans = strv_join(args, " ");
ans = quote_command_line(args, shflags);
if (!ans)
return -ENOMEM;
} else {
/* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++)
@ -257,7 +255,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
ans = str_realloc(ans);
}
*line = ans;
*ret = ans;
return 0;
}
@ -464,29 +462,29 @@ int is_kernel_thread(pid_t pid) {
return !!(flags & PF_KTHREAD);
}
int get_process_capeff(pid_t pid, char **capeff) {
int get_process_capeff(pid_t pid, char **ret) {
const char *p;
int r;
assert(capeff);
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "status");
r = get_proc_field(p, "CapEff", WHITESPACE, capeff);
r = get_proc_field(p, "CapEff", WHITESPACE, ret);
if (r == -ENOENT)
return -ESRCH;
return r;
}
static int get_process_link_contents(const char *proc_file, char **name) {
static int get_process_link_contents(const char *proc_file, char **ret) {
int r;
assert(proc_file);
assert(name);
assert(ret);
r = readlink_malloc(proc_file, name);
r = readlink_malloc(proc_file, ret);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
@ -495,32 +493,33 @@ static int get_process_link_contents(const char *proc_file, char **name) {
return 0;
}
int get_process_exe(pid_t pid, char **name) {
int get_process_exe(pid_t pid, char **ret) {
const char *p;
char *d;
int r;
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "exe");
r = get_process_link_contents(p, name);
r = get_process_link_contents(p, ret);
if (r < 0)
return r;
d = endswith(*name, " (deleted)");
d = endswith(*ret, " (deleted)");
if (d)
*d = '\0';
return 0;
}
static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
_cleanup_fclose_ FILE *f = NULL;
const char *p;
int r;
assert(field);
assert(uid);
assert(ret);
if (pid < 0)
return -EINVAL;
@ -550,60 +549,62 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
l[strcspn(l, WHITESPACE)] = 0;
return parse_uid(l, uid);
return parse_uid(l, ret);
}
}
return -EIO;
}
int get_process_uid(pid_t pid, uid_t *uid) {
int get_process_uid(pid_t pid, uid_t *ret) {
if (pid == 0 || pid == getpid_cached()) {
*uid = getuid();
*ret = getuid();
return 0;
}
return get_process_id(pid, "Uid:", uid);
return get_process_id(pid, "Uid:", ret);
}
int get_process_gid(pid_t pid, gid_t *gid) {
int get_process_gid(pid_t pid, gid_t *ret) {
if (pid == 0 || pid == getpid_cached()) {
*gid = getgid();
*ret = getgid();
return 0;
}
assert_cc(sizeof(uid_t) == sizeof(gid_t));
return get_process_id(pid, "Gid:", gid);
return get_process_id(pid, "Gid:", ret);
}
int get_process_cwd(pid_t pid, char **cwd) {
int get_process_cwd(pid_t pid, char **ret) {
const char *p;
assert(pid >= 0);
assert(ret);
if (pid == 0 || pid == getpid_cached())
return safe_getcwd(cwd);
return safe_getcwd(ret);
p = procfs_file_alloca(pid, "cwd");
return get_process_link_contents(p, cwd);
return get_process_link_contents(p, ret);
}
int get_process_root(pid_t pid, char **root) {
int get_process_root(pid_t pid, char **ret) {
const char *p;
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "root");
return get_process_link_contents(p, root);
return get_process_link_contents(p, ret);
}
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
int get_process_environ(pid_t pid, char **env) {
int get_process_environ(pid_t pid, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *outcome = NULL;
size_t sz = 0;
@ -611,7 +612,7 @@ int get_process_environ(pid_t pid, char **env) {
int r;
assert(pid >= 0);
assert(env);
assert(ret);
p = procfs_file_alloca(pid, "environ");
@ -643,7 +644,7 @@ int get_process_environ(pid_t pid, char **env) {
}
outcome[sz] = '\0';
*env = TAKE_PTR(outcome);
*ret = TAKE_PTR(outcome);
return 0;
}
@ -702,13 +703,13 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0;
}
int get_process_umask(pid_t pid, mode_t *umask) {
int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL;
const char *p;
int r;
assert(umask);
assert(pid >= 0);
assert(ret);
p = procfs_file_alloca(pid, "status");
@ -716,7 +717,7 @@ int get_process_umask(pid_t pid, mode_t *umask) {
if (r == -ENOENT)
return -ESRCH;
return parse_mode(m, umask);
return parse_mode(m, ret);
}
int wait_for_terminate(pid_t pid, siginfo_t *status) {
@ -830,7 +831,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
if (n >= until)
break;
r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0;
r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)));
/* Assuming we woke due to the child exiting. */
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
if (status.si_pid == pid) {
@ -863,8 +864,8 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
void sigkill_wait(pid_t pid) {
assert(pid > 1);
if (kill(pid, SIGKILL) >= 0)
(void) wait_for_terminate(pid, NULL);
(void) kill(pid, SIGKILL);
(void) wait_for_terminate(pid, NULL);
}
void sigkill_waitp(pid_t *pid) {
@ -881,14 +882,14 @@ void sigkill_waitp(pid_t *pid) {
void sigterm_wait(pid_t pid) {
assert(pid > 1);
if (kill_and_sigcont(pid, SIGTERM) >= 0)
(void) wait_for_terminate(pid, NULL);
(void) kill_and_sigcont(pid, SIGTERM);
(void) wait_for_terminate(pid, NULL);
}
int kill_and_sigcont(pid_t pid, int sig) {
int r;
r = kill(pid, sig) < 0 ? -errno : 0;
r = RET_NERRNO(kill(pid, sig));
/* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
* affected by a process being suspended anyway. */
@ -1064,8 +1065,8 @@ unsigned long personality_from_string(const char *p) {
if (architecture == native_architecture())
return PER_LINUX;
#ifdef SECONDARY_ARCHITECTURE
if (architecture == SECONDARY_ARCHITECTURE)
#ifdef ARCHITECTURE_SECONDARY
if (architecture == ARCHITECTURE_SECONDARY)
return PER_LINUX32;
#endif
@ -1077,9 +1078,9 @@ const char* personality_to_string(unsigned long p) {
if (p == PER_LINUX)
architecture = native_architecture();
#ifdef SECONDARY_ARCHITECTURE
#ifdef ARCHITECTURE_SECONDARY
else if (p == PER_LINUX32)
architecture = SECONDARY_ARCHITECTURE;
architecture = ARCHITECTURE_SECONDARY;
#endif
if (architecture < 0)
@ -1150,23 +1151,6 @@ int pid_compare_func(const pid_t *a, const pid_t *b) {
/* Suitable for usage in qsort() */
return CMP(*a, *b);
}
int ioprio_parse_priority(const char *s, int *ret) {
int i, r;
assert(s);
assert(ret);
r = safe_atoi(s, &i);
if (r < 0)
return r;
if (!ioprio_priority_is_valid(i))
return -EINVAL;
*ret = i;
return 0;
}
#endif /* NM_IGNORED */
/* The cached PID, possible values:
@ -1256,7 +1240,7 @@ static void restore_sigsetp(sigset_t **ssp) {
int safe_fork_full(
const char *name,
int except_fds[],
const int except_fds[],
size_t n_except_fds,
ForkFlags flags,
pid_t *ret_pid) {
@ -1451,7 +1435,7 @@ int safe_fork_full(
int namespace_fork(
const char *outer_name,
const char *inner_name,
int except_fds[],
const int except_fds[],
size_t n_except_fds,
ForkFlags flags,
int pidns_fd,
@ -1467,8 +1451,7 @@ int namespace_fork(
* process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
* /proc/self/fd works correctly. */
r = safe_fork_full(outer_name, except_fds, n_except_fds,
(flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
if (r < 0)
return r;
if (r == 0) {
@ -1513,7 +1496,7 @@ int set_oom_score_adjust(int value) {
}
int get_oom_score_adjust(int *ret) {
_cleanup_free_ char *t;
_cleanup_free_ char *t = NULL;
int r, a;
r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
@ -1628,14 +1611,27 @@ bool invoked_as(char *argv[], const char *token) {
return strstr(last_path_component(argv[0]), token);
}
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
[IOPRIO_CLASS_BE] = "best-effort",
[IOPRIO_CLASS_IDLE] = "idle",
};
_noreturn_ void freeze(void) {
log_close();
/* Make sure nobody waits for us (i.e. on one of our sockets) anymore. Note that we use
* close_all_fds_without_malloc() instead of plain close_all_fds() here, since we want this function
* to be compatible with being called from signal handlers. */
(void) close_all_fds_without_malloc(NULL, 0);
/* Let's not freeze right away, but keep reaping zombies. */
for (;;) {
siginfo_t si = {};
if (waitid(P_ALL, 0, &si, WEXITED) < 0 && errno != EINTR)
break;
}
/* waitid() failed with an unexpected error, things are really borked. Freeze now! */
for (;;)
pause();
}
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",

View File

@ -14,7 +14,6 @@
#include "alloc-util.h"
#include "format-util.h"
#include "macro.h"
#include "missing_ioprio.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@ -39,17 +38,17 @@ typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
} ProcessCmdlineFlags;
int get_process_comm(pid_t pid, char **name);
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line);
int get_process_exe(pid_t pid, char **name);
int get_process_uid(pid_t pid, uid_t *uid);
int get_process_gid(pid_t pid, gid_t *gid);
int get_process_capeff(pid_t pid, char **capeff);
int get_process_cwd(pid_t pid, char **cwd);
int get_process_root(pid_t pid, char **root);
int get_process_environ(pid_t pid, char **environ);
int get_process_ppid(pid_t pid, pid_t *ppid);
int get_process_umask(pid_t pid, mode_t *umask);
int get_process_comm(pid_t pid, char **ret);
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
int get_process_exe(pid_t pid, char **ret);
int get_process_uid(pid_t pid, uid_t *ret);
int get_process_gid(pid_t pid, gid_t *ret);
int get_process_capeff(pid_t pid, char **ret);
int get_process_cwd(pid_t pid, char **ret);
int get_process_root(pid_t pid, char **ret);
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 wait_for_terminate(pid_t pid, siginfo_t *status);
@ -97,9 +96,6 @@ const char *personality_to_string(unsigned long);
int safe_personality(unsigned long p);
int opinionated_personality(unsigned long *ret);
int ioprio_class_to_string_alloc(int i, char **s);
int ioprio_class_from_string(const char *s);
const char *sigchld_code_to_string(int i) _const_;
int sigchld_code_from_string(const char *s) _pure_;
@ -131,21 +127,11 @@ static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
}
static inline bool ioprio_class_is_valid(int i) {
return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE);
}
static inline bool ioprio_priority_is_valid(int i) {
return i >= 0 && i < IOPRIO_BE_NR;
}
static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
#endif /* NM_IGNORED */
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
void reset_cached_pid(void);
@ -168,13 +154,13 @@ typedef enum ForkFlags {
FORK_NEW_USERNS = 1 << 13, /* Run child in its own user namespace */
} ForkFlags;
int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, 0, flags, ret_pid);
}
int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int set_oom_score_adjust(int value);
int get_oom_score_adjust(int *ret);
@ -194,8 +180,9 @@ 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 _pid_ = (pid); \
(pid) = 0; \
pid_t *_ppid_ = &(pid); \
pid_t _pid_ = *_ppid_; \
*_ppid_ = 0; \
_pid_; \
})
@ -204,3 +191,5 @@ int pidfd_get_pid(int fd, pid_t *ret);
int setpriority_closest(int priority);
bool invoked_as(char *argv[], const char *token);
_noreturn_ void freeze(void);

View File

@ -163,7 +163,6 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
static int have_syscall = -1;
_cleanup_close_ int fd = -1;
bool got_some = false;
int r;
/* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
* the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
@ -222,25 +221,26 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
for (;;) {
ssize_t l;
#if !HAVE_GETRANDOM
/* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround.
* If you don't compile against a libc that provides getrandom(), you don't get it. */
r = -1;
l = -1;
errno = ENOSYS;
#else
r = getrandom(p, n,
l = getrandom(p, n,
(FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
(FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
#endif
if (r > 0) {
if (l > 0) {
have_syscall = true;
if ((size_t) r == n)
if ((size_t) l == n)
return 0; /* Yay, success! */
assert((size_t) r < n);
p = (uint8_t*) p + r;
n -= r;
assert((size_t) l < n);
p = (uint8_t*) p + l;
n -= l;
if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
/* Fill in the remaining bytes using pseudo-random values */
@ -257,7 +257,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
/* Fill in the rest with /dev/urandom */
break;
} else if (r == 0) {
} else if (l == 0) {
have_syscall = true;
return -EIO;

View File

@ -5,6 +5,7 @@
#include <errno.h>
#include <stdarg.h>
#include "errno-util.h"
#include "macro.h"
#include "parse-util.h"
#include "signal-util.h"
@ -42,10 +43,7 @@ int reset_signal_mask(void) {
if (sigemptyset(&ss) < 0)
return -errno;
if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
return -errno;
return 0;
return RET_NERRNO(sigprocmask(SIG_SETMASK, &ss, NULL));
}
int sigaction_many_internal(const struct sigaction *sa, ...) {
@ -122,7 +120,7 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
return 0;
}
static const char *const __signal_table[] = {
static const char *const static_signal_table[] = {
[SIGHUP] = "HUP",
[SIGINT] = "INT",
[SIGQUIT] = "QUIT",
@ -158,13 +156,13 @@ static const char *const __signal_table[] = {
[SIGSYS] = "SYS"
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
const char *signal_to_string(int signo) {
static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
const char *name;
name = __signal_to_string(signo);
name = static_signal_to_string(signo);
if (name)
return name;
@ -193,7 +191,7 @@ int signal_from_string(const char *s) {
s += 3;
/* Check that the input is a signal name. */
signo = __signal_from_string(s);
signo = static_signal_from_string(s);
if (signo > 0)
return signo;
@ -250,11 +248,7 @@ int signal_is_blocked(int sig) {
if (r != 0)
return -r;
r = sigismember(&ss, sig);
if (r < 0)
return -errno;
return r;
return RET_NERRNO(sigismember(&ss, sig));
}
int pop_pending_signal_internal(int sig, ...) {

View File

@ -405,6 +405,41 @@ const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) {
}
}
int sockaddr_set_in_addr(
union sockaddr_union *u,
int family,
const union in_addr_union *a,
uint16_t port) {
assert(u);
assert(a);
switch (family) {
case AF_INET:
u->in = (struct sockaddr_in) {
.sin_family = AF_INET,
.sin_addr = a->in,
.sin_port = htobe16(port),
};
return 0;
case AF_INET6:
u->in6 = (struct sockaddr_in6) {
.sin6_family = AF_INET6,
.sin6_addr = a->in6,
.sin6_port = htobe16(port),
};
return 0;
default:
return -EAFNOSUPPORT;
}
}
int sockaddr_pretty(
const struct sockaddr *_sa,
socklen_t salen,
@ -556,7 +591,7 @@ int getpeername_pretty(int fd, bool include_port, char **ret) {
return -errno;
if (sa.sa.sa_family == AF_UNIX) {
struct ucred ucred = {};
struct ucred ucred = UCRED_INVALID;
/* UNIX connection sockets are anonymous, so let's use
* PID/UID as pretty credentials instead */
@ -1238,10 +1273,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
/* Call with NULL to drop binding */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0)
return -errno;
return 0;
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)));
}
int socket_bind_to_ifindex(int fd, int ifindex) {
@ -1250,13 +1282,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
assert(fd >= 0);
if (ifindex <= 0) {
if (ifindex <= 0)
/* Drop binding */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0)
return -errno;
return 0;
}
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
if (r != -ENOPROTOOPT)
@ -1345,16 +1373,10 @@ int socket_set_unicast_if(int fd, int af, int ifi) {
switch (af) {
case AF_INET:
if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
return -errno;
return 0;
return RET_NERRNO(setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
case AF_INET6:
if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
return -errno;
return 0;
return RET_NERRNO(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
default:
return -EAFNOSUPPORT;

View File

@ -15,6 +15,7 @@
#include <sys/un.h>
#include "errno-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "missing_network.h"
#include "missing_socket.h"
@ -108,6 +109,7 @@ bool socket_ipv6_is_enabled(void);
int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa);
int sockaddr_set_in_addr(union sockaddr_union *u, int family, const union in_addr_union *a, uint16_t port);
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret);
@ -331,3 +333,6 @@ static inline int socket_set_recvfragsize(int fd, int af, bool b) {
#endif /* NM_IGNORED */
int socket_get_mtu(int fd, int af, size_t *ret);
/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }

View File

@ -5,14 +5,20 @@
#include "macro.h"
/* This is the same as glibc's internal __compar_d_fn_t type. glibc exports a public comparison_fn_t, for the
* external type __compar_fn_t, but doesn't do anything similar for __compar_d_fn_t. Let's hence do that
* ourselves, picking a name that is obvious, but likely enough to not clash with glibc's choice of naming if
* they should ever add one. */
typedef int (*comparison_userdata_fn_t)(const void *, const void *, void *);
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
__compar_d_fn_t compar, void *arg);
comparison_userdata_fn_t compar, void *arg);
#define typesafe_bsearch_r(k, b, n, func, userdata) \
({ \
const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
/**
@ -20,7 +26,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
* that only if nmemb > 0.
*/
static inline void* bsearch_safe(const void *key, const void *base,
size_t nmemb, size_t size, __compar_fn_t compar) {
size_t nmemb, size_t size, comparison_fn_t compar) {
if (nmemb <= 0)
return NULL;
@ -32,14 +38,14 @@ static inline void* bsearch_safe(const void *key, const void *base,
({ \
const typeof(b[0]) *_k = k; \
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
})
/**
* Normal qsort requires base to be nonnull. Here were require
* that only if nmemb > 0.
*/
static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
if (nmemb <= 1)
return;
@ -52,11 +58,11 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_f
#define typesafe_qsort(p, n, func) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
_qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
_qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
})
#if 0 /* NM_IGNORED */
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
if (nmemb <= 1)
return;
@ -67,7 +73,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_
#define typesafe_qsort_r(p, n, func, userdata) \
({ \
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
})
#endif /* NM_IGNORED */

View File

@ -55,9 +55,9 @@ typedef uint64_t __sd_bitwise be64_t;
#undef le64toh
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define bswap_16_on_le(x) __bswap_16(x)
#define bswap_32_on_le(x) __bswap_32(x)
#define bswap_64_on_le(x) __bswap_64(x)
#define bswap_16_on_le(x) bswap_16(x)
#define bswap_32_on_le(x) bswap_32(x)
#define bswap_64_on_le(x) bswap_64(x)
#define bswap_16_on_be(x) (x)
#define bswap_32_on_be(x) (x)
#define bswap_64_on_be(x) (x)
@ -65,9 +65,9 @@ typedef uint64_t __sd_bitwise be64_t;
#define bswap_16_on_le(x) (x)
#define bswap_32_on_le(x) (x)
#define bswap_64_on_le(x) (x)
#define bswap_16_on_be(x) __bswap_16(x)
#define bswap_32_on_be(x) __bswap_32(x)
#define bswap_64_on_be(x) __bswap_64(x)
#define bswap_16_on_be(x) bswap_16(x)
#define bswap_32_on_be(x) bswap_32(x)
#define bswap_64_on_be(x) bswap_64(x)
#endif
static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }

View File

@ -15,10 +15,13 @@
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "filesystems.h"
#include "fs-util.h"
#include "macro.h"
#include "missing_fs.h"
#include "missing_magic.h"
#include "missing_syscall.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "stat-util.h"
#include "string-util.h"
@ -75,27 +78,41 @@ int is_device_node(const char *path) {
int dir_is_empty_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -1;
_cleanup_closedir_ DIR *d = NULL;
/* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
DEFINE_DIRENT_BUFFER(buffer, 3);
struct dirent *de;
ssize_t n;
if (path) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else if (dir_fd == AT_FDCWD) {
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else {
/* Note that DUPing is not enough, as the internal pointer
* would still be shared and moved by FOREACH_DIRENT. */
fd = fd_reopen(dir_fd, O_CLOEXEC);
/* Note that DUPing is not enough, as the internal pointer would still be shared and moved
* getedents64(). */
assert(dir_fd >= 0);
fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return fd;
}
d = take_fdopendir(&fd);
if (!d)
n = getdents64(fd, &buffer, sizeof(buffer));
if (n < 0)
return -errno;
FOREACH_DIRENT(de, d, return -errno)
return 0;
msan_unpoison(&buffer, n);
FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
if (!dot_or_dot_dot(de->d_name))
return 0;
return 1;
}
@ -178,7 +195,6 @@ int files_same(const char *filea, const char *fileb, int flags) {
a.st_ino == b.st_ino;
}
#if 0 /* NM_IGNORED */
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
assert(s);
assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
@ -186,6 +202,7 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
return F_TYPE_EQUAL(s->f_type, magic_value);
}
#if 0 /* NM_IGNORED */
int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
struct statfs s;
@ -194,6 +211,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
return is_fs_type(&s, magic_value);
}
#endif /* NM_IGNORE */
int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
struct statfs s;
@ -204,20 +222,13 @@ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
return is_fs_type(&s, magic_value);
}
#if 0 /* NM_IGNORE */
bool is_temporary_fs(const struct statfs *s) {
return is_fs_type(s, TMPFS_MAGIC) ||
is_fs_type(s, RAMFS_MAGIC);
return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
}
bool is_network_fs(const struct statfs *s) {
return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
is_fs_type(s, CODA_SUPER_MAGIC) ||
is_fs_type(s, NCP_SUPER_MAGIC) ||
is_fs_type(s, NFS_SUPER_MAGIC) ||
is_fs_type(s, SMB_SUPER_MAGIC) ||
is_fs_type(s, V9FS_MAGIC) ||
is_fs_type(s, AFS_SUPER_MAGIC) ||
is_fs_type(s, OCFS2_SUPER_MAGIC);
return fs_in_group(s, FILESYSTEM_SET_NETWORK);
}
int fd_is_temporary_fs(int fd) {
@ -395,6 +406,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
return 0;
}
#endif /* NM_IGNORED */
int proc_mounted(void) {
int r;
@ -408,6 +420,7 @@ int proc_mounted(void) {
return r;
}
#if 0 /* NM_IGNORED */
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
/* Returns if the specified stat structures reference the same, unmodified inode. This check tries to

View File

@ -60,6 +60,10 @@ static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
static inline const char *empty_to_na(const char *p) {
return isempty(p) ? "n/a" : p;
}
static inline const char *empty_to_dash(const char *str) {
return isempty(str) ? "-" : str;
}

View File

@ -18,7 +18,7 @@
#include "string-util.h"
#include "strv.h"
char *strv_find(char * const *l, const char *name) {
char* strv_find(char * const *l, const char *name) {
char * const *i;
assert(name);
@ -30,7 +30,7 @@ char *strv_find(char * const *l, const char *name) {
return NULL;
}
char *strv_find_case(char * const *l, const char *name) {
char* strv_find_case(char * const *l, const char *name) {
char * const *i;
assert(name);
@ -42,7 +42,7 @@ char *strv_find_case(char * const *l, const char *name) {
return NULL;
}
char *strv_find_prefix(char * const *l, const char *name) {
char* strv_find_prefix(char * const *l, const char *name) {
char * const *i;
assert(name);
@ -54,7 +54,7 @@ char *strv_find_prefix(char * const *l, const char *name) {
return NULL;
}
char *strv_find_startswith(char * const *l, const char *name) {
char* strv_find_startswith(char * const *l, const char *name) {
char * const *i, *e;
assert(name);
@ -71,19 +71,17 @@ char *strv_find_startswith(char * const *l, const char *name) {
return NULL;
}
char **strv_free(char **l) {
char **k;
char** strv_free(char **l) {
if (!l)
return NULL;
for (k = l; *k; k++)
for (char **k = l; *k; k++)
free(*k);
return mfree(l);
}
char **strv_free_erase(char **l) {
char** strv_free_erase(char **l) {
char **i;
STRV_FOREACH(i, l)
@ -92,7 +90,7 @@ char **strv_free_erase(char **l) {
return mfree(l);
}
char **strv_copy(char * const *l) {
char** strv_copy(char * const *l) {
char **r, **k;
k = r = new(char*, strv_length(l) + 1);
@ -124,7 +122,7 @@ size_t strv_length(char * const *l) {
return n;
}
char **strv_new_ap(const char *x, va_list ap) {
char** strv_new_ap(const char *x, va_list ap) {
_cleanup_strv_free_ char **a = NULL;
size_t n = 0, i = 0;
va_list aq;
@ -163,7 +161,7 @@ char **strv_new_ap(const char *x, va_list ap) {
return TAKE_PTR(a);
}
char **strv_new_internal(const char *x, ...) {
char** strv_new_internal(const char *x, ...) {
char **r;
va_list ap;
@ -176,7 +174,7 @@ char **strv_new_internal(const char *x, ...) {
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
char * const *s, **t;
size_t p, q, i = 0, j;
size_t p, q, i = 0;
assert(a);
@ -197,7 +195,6 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
*a = t;
STRV_FOREACH(s, b) {
if (filter_duplicates && strv_contains(t, *s))
continue;
@ -214,7 +211,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
return (int) i;
rollback:
for (j = 0; j < i; j++)
for (size_t j = 0; j < i; j++)
free(t[p + j]);
t[p] = NULL;
@ -289,7 +286,6 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
return -ENOMEM;
l[n++] = TAKE_PTR(word);
l[n] = NULL;
}
@ -376,7 +372,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
}
#endif /* NM_IGNORED */
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
char * const *s;
char *r, *e;
size_t n, k, m;
@ -483,7 +479,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
int strv_insert(char ***l, size_t position, char *value) {
char **c;
size_t n, m, i;
size_t n, m;
if (!value)
return 0;
@ -500,18 +496,14 @@ int strv_insert(char ***l, size_t position, char *value) {
if (!c)
return -ENOMEM;
for (i = 0; i < position; i++)
for (size_t i = 0; i < position; i++)
c[i] = (*l)[i];
c[position] = value;
for (i = position; i < n; i++)
for (size_t i = position; i < n; i++)
c[i+1] = (*l)[i];
c[n+1] = NULL;
free(*l);
*l = c;
return 0;
return free_and_replace(*l, c);
}
int strv_consume(char ***l, char *value) {
@ -608,7 +600,7 @@ int strv_extend_front(char ***l, const char *value) {
return 0;
}
char **strv_uniq(char **l) {
char** strv_uniq(char **l) {
char **i;
/* Drops duplicate entries. The first identical string will be
@ -630,7 +622,7 @@ bool strv_is_uniq(char * const *l) {
return true;
}
char **strv_remove(char **l, const char *s) {
char** strv_remove(char **l, const char *s) {
char **f, **t;
if (!l)
@ -651,7 +643,7 @@ char **strv_remove(char **l, const char *s) {
return l;
}
char **strv_parse_nulstr(const char *s, size_t 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,
@ -663,7 +655,6 @@ char **strv_parse_nulstr(const char *s, size_t l) {
* empty strings in s.
*/
const char *p;
size_t c = 0, i = 0;
char **v;
@ -672,7 +663,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
if (l <= 0)
return new0(char*, 1);
for (p = s; p < s + l; p++)
for (const char *p = s; p < s + l; p++)
if (*p == 0)
c++;
@ -683,8 +674,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
if (!v)
return NULL;
p = s;
while (p < s + l) {
for (const char *p = s; p < s + l; ) {
const char *e;
e = memchr(p, 0, s + l - p);
@ -709,7 +699,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
}
#if 0 /* NM_IGNORED */
char **strv_split_nulstr(const char *s) {
char** strv_split_nulstr(const char *s) {
const char *i;
char **r = NULL;
@ -786,7 +776,7 @@ static int str_compare(char * const *a, char * const *b) {
return strcmp(*a, *b);
}
char **strv_sort(char **l) {
char** strv_sort(char **l) {
typesafe_qsort(l, strv_length(l), str_compare);
return l;
}
@ -836,21 +826,21 @@ int strv_extendf(char ***l, const char *format, ...) {
return strv_consume(l, x);
}
char **strv_reverse(char **l) {
size_t n, i;
char** strv_reverse(char **l) {
size_t n;
n = strv_length(l);
if (n <= 1)
return l;
for (i = 0; i < n / 2; i++)
for (size_t i = 0; i < n / 2; i++)
SWAP_TWO(l[i], l[n-1-i]);
return l;
}
#if 0 /* NM_IGNORED */
char **strv_shell_escape(char **l, const char *bad) {
char** strv_shell_escape(char **l, const char *bad) {
char **s;
/* Escapes every character in every string in l that is in bad,
@ -882,19 +872,7 @@ bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *
return false;
}
char ***strv_free_free(char ***l) {
char ***i;
if (!l)
return NULL;
for (i = l; *i; i++)
strv_free(*i);
return mfree(l);
}
char **strv_skip(char **l, size_t n) {
char** strv_skip(char **l, size_t n) {
while (n > 0) {
if (strv_isempty(l))
@ -907,7 +885,7 @@ char **strv_skip(char **l, size_t n) {
}
int strv_extend_n(char ***l, const char *value, size_t n) {
size_t i, j, k;
size_t i, k;
char **nl;
assert(l);
@ -934,15 +912,15 @@ int strv_extend_n(char ***l, const char *value, size_t n) {
if (!nl[i])
goto rollback;
}
nl[i] = NULL;
return 0;
rollback:
for (j = k; j < i; j++)
for (size_t j = k; j < i; j++)
free(nl[j]);
nl[k] = NULL;
return -ENOMEM;
}

View File

@ -13,23 +13,23 @@
#include "macro.h"
#include "string-util.h"
char *strv_find(char * const *l, const char *name) _pure_;
char *strv_find_case(char * const *l, const char *name) _pure_;
char *strv_find_prefix(char * const *l, const char *name) _pure_;
char *strv_find_startswith(char * const *l, const char *name) _pure_;
char* strv_find(char * const *l, const char *name) _pure_;
char* strv_find_case(char * const *l, const char *name) _pure_;
char* strv_find_prefix(char * const *l, const char *name) _pure_;
char* strv_find_startswith(char * const *l, const char *name) _pure_;
#define strv_contains(l, s) (!!strv_find((l), (s)))
#define strv_contains_case(l, s) (!!strv_find_case((l), (s)))
char **strv_free(char **l);
char** strv_free(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
char **strv_free_erase(char **l);
char** strv_free_erase(char **l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
char **strv_copy(char * const *l);
char** strv_copy(char * const *l);
size_t strv_length(char * const *l) _pure_;
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@ -50,8 +50,8 @@ int strv_consume(char ***l, char *value);
int strv_consume_pair(char ***l, char *a, char *b);
int strv_consume_prepend(char ***l, char *value);
char **strv_remove(char **l, const char *s);
char **strv_uniq(char **l);
char** strv_remove(char **l, const char *s);
char** strv_uniq(char **l);
bool strv_is_uniq(char * const *l);
int strv_compare(char * const *a, char * const *b);
@ -59,8 +59,8 @@ static inline bool strv_equal(char * const *a, char * const *b) {
return strv_compare(a, b) == 0;
}
char **strv_new_internal(const char *x, ...) _sentinel_;
char **strv_new_ap(const char *x, va_list ap);
char** strv_new_internal(const char *x, ...) _sentinel_;
char** strv_new_ap(const char *x, va_list ap);
#define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
#define STRV_IGNORE ((const char *) POINTER_MAX)
@ -74,7 +74,7 @@ static inline bool strv_isempty(char * const *l) {
}
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags);
static inline char **strv_split(const char *s, const char *separators) {
static inline char** strv_split(const char *s, const char *separators) {
char **ret;
if (strv_split_full(&ret, s, separators, 0) < 0)
@ -87,7 +87,7 @@ int strv_split_and_extend_full(char ***t, const char *s, const char *separators,
#define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0)
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
static inline char **strv_split_newlines(const char *s) {
static inline char** strv_split_newlines(const char *s) {
char **ret;
if (strv_split_newlines_full(&ret, s, 0) < 0)
@ -101,13 +101,13 @@ static inline char **strv_split_newlines(const char *s) {
* string in the vector is an empty string. */
int strv_split_colon_pairs(char ***t, const char *s);
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor);
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor);
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);
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) {
@ -136,7 +136,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
#define STRV_FOREACH_PAIR(x, y, l) \
for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
char **strv_sort(char **l);
char** strv_sort(char **l);
void strv_print(char * const *l);
#define strv_from_stdarg_alloca(first) \
@ -206,13 +206,16 @@ void strv_print(char * const *l);
_found; \
})
#define FOREACH_STRING(x, y, ...) \
for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \
#define _FOREACH_STRING(uniq, x, y, ...) \
for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \
x; \
x = *(++_l))
x = *(++UNIQ_T(l, uniq)))
char **strv_reverse(char **l);
char **strv_shell_escape(char **l, const char *bad);
#define FOREACH_STRING(x, y, ...) \
_FOREACH_STRING(UNIQ, x, y, ##__VA_ARGS__)
char** strv_reverse(char **l);
char** strv_shell_escape(char **l, const char *bad);
bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos);
static inline bool strv_fnmatch(char* const* patterns, const char *s) {
@ -225,10 +228,7 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
strv_fnmatch_full(patterns, s, flags, NULL);
}
char ***strv_free_free(char ***l);
DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free);
char **strv_skip(char **l, size_t n);
char** strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);

View File

@ -17,57 +17,73 @@
#include <stdio.h>
#include <string.h>
#include "string-util.h"
#include "strxcpyx.h"
size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
bool truncated = false;
assert(dest);
assert(src);
if (size == 0)
if (size == 0) {
if (ret_truncated)
*ret_truncated = len > 0;
return 0;
}
if (len >= size) {
if (size > 1)
*dest = mempcpy(*dest, src, size-1);
size = 0;
truncated = true;
} else if (len > 0) {
*dest = mempcpy(*dest, src, len);
size -= len;
}
if (ret_truncated)
*ret_truncated = truncated;
*dest[0] = '\0';
return size;
}
size_t strpcpy(char **dest, size_t size, const char *src) {
size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) {
assert(dest);
assert(src);
return strnpcpy(dest, size, src, strlen(src));
return strnpcpy_full(dest, size, src, strlen(src), ret_truncated);
}
size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va;
int i;
assert(dest);
assert(src);
if (size == 0)
return 0;
va_start(va, src);
i = vsnprintf(*dest, size, src, va);
if (i < (int)size) {
va_end(va);
if (i < (int) size) {
*dest += i;
size -= i;
} else
} else {
size = 0;
va_end(va);
truncated = i > 0;
}
if (ret_truncated)
*ret_truncated = truncated;
return size;
}
size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va;
assert(dest);
@ -75,31 +91,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
va_start(va, src);
do {
size = strpcpy(dest, size, src);
bool t;
size = strpcpy_full(dest, size, src, &t);
truncated = truncated || t;
src = va_arg(va, char *);
} while (src);
va_end(va);
if (ret_truncated)
*ret_truncated = truncated;
return size;
}
size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
char *s;
assert(dest);
assert(src);
s = dest;
return strnpcpy(&s, size, src, len);
return strnpcpy_full(&s, size, src, len, ret_truncated);
}
size_t strscpy(char *dest, size_t size, const char *src) {
size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) {
assert(dest);
assert(src);
return strnscpy(dest, size, src, strlen(src));
return strnscpy_full(dest, size, src, strlen(src), ret_truncated);
}
size_t strscpyl(char *dest, size_t size, const char *src, ...) {
size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va;
char *s;
@ -109,10 +132,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
va_start(va, src);
s = dest;
do {
size = strpcpy(&s, size, src);
bool t;
size = strpcpy_full(&s, size, src, &t);
truncated = truncated || t;
src = va_arg(va, char *);
} while (src);
va_end(va);
if (ret_truncated)
*ret_truncated = truncated;
return size;
}

View File

@ -1,14 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "macro.h"
size_t strnpcpy(char **dest, size_t size, const char *src, size_t len);
size_t strpcpy(char **dest, size_t size, const char *src);
size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4);
size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_;
size_t strnscpy(char *dest, size_t size, const char *src, size_t len);
size_t strscpy(char *dest, size_t size, const char *src);
size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_;
size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated);
static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
return strnpcpy_full(dest, size, src, len, NULL);
}
size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated);
static inline size_t strpcpy(char **dest, size_t size, const char *src) {
return strpcpy_full(dest, size, src, NULL);
}
size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5);
#define strpcpyf(dest, size, src, ...) \
strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__)
size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
#define strpcpyl(dest, size, src, ...) \
strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__)
size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated);
static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
return strnscpy_full(dest, size, src, len, NULL);
}
size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated);
static inline size_t strscpy(char *dest, size_t size, const char *src) {
return strscpy_full(dest, size, src, NULL);
}
size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
#define strscpyl(dest, size, src, ...) \
strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__)

View File

@ -271,7 +271,6 @@ struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
return ts;
}
#if 0 /* NM_IGNORED */
usec_t timeval_load(const struct timeval *tv) {
assert(tv);
@ -286,6 +285,7 @@ usec_t timeval_load(const struct timeval *tv) {
(usec_t) tv->tv_usec;
}
#if 0 /* NM_IGNORED */
struct timeval *timeval_store(struct timeval *tv, usec_t u) {
assert(tv);
@ -676,7 +676,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
goto finish;
} else if ((k = endswith(t, " ago"))) {
t = strndupa(t, k - t);
t = strndupa_safe(t, k - t);
r = parse_sec(t, &minus);
if (r < 0)
@ -685,7 +685,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
goto finish;
} else if ((k = endswith(t, " left"))) {
t = strndupa(t, k - t);
t = strndupa_safe(t, k - t);
r = parse_sec(t, &plus);
if (r < 0)
@ -697,7 +697,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
/* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC");
if (utc)
t = strndupa(t, utc - t);
t = strndupa_safe(t, utc - t);
else {
const char *e = NULL;
int j;
@ -728,7 +728,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
if (IN_SET(j, 0, 1)) {
/* Found one of the two timezones specified. */
t = strndupa(t, e - t - 1);
t = strndupa_safe(t, e - t - 1);
dst = j;
tzn = tzname[j];
}
@ -929,7 +929,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
/* Cut off the timezone if we don't need it. */
if (with_tz)
t = strndupa(t, last_space - t);
t = strndupa_safe(t, last_space - t);
shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);

View File

@ -67,16 +67,9 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
/* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) {
int fd = -1; /* avoid false maybe-uninitialized warning */
assert(pattern);
RUN_WITH_UMASK(0077)
fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
return fd;
BLOCK_WITH_UMASK(0077);
return RET_NERRNO(mkostemp(pattern, O_CLOEXEC));
}
#if 0 /* NM_IGNORED */
@ -288,8 +281,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
}
int link_tmpfile(int fd, const char *path, const char *target) {
int r;
assert(fd >= 0);
assert(target);
@ -300,16 +291,10 @@ int link_tmpfile(int fd, const char *path, const char *target) {
* Note that in both cases we will not replace existing files. This is because linkat() does not support this
* operation currently (renameat2() does), and there is no nice way to emulate this. */
if (path) {
r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
if (r < 0)
return r;
} else {
if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
return -errno;
}
if (path)
return rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
return 0;
return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW));
}
int mkdtemp_malloc(const char *template, char **ret) {

View File

@ -24,3 +24,6 @@ assert_cc((S_IFMT & 0777) == 0);
for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
FLAGS_SET(_saved_umask_, S_IFMT); \
_saved_umask_ &= 0777)
#define BLOCK_WITH_UMASK(mask) \
_unused_ _cleanup_umask_ mode_t _saved_umask_ = umask(mask);

View File

@ -112,6 +112,12 @@ bool is_nologin_shell(const char *shell);
int is_this_me(const char *username);
const char *get_home_root(void);
static inline bool hashed_password_is_locked_or_invalid(const char *password) {
return password && password[0] != '$';
}
/* A locked *and* invalid password for "struct spwd"'s .sp_pwdp and "struct passwd"'s .pw_passwd field */
#define PASSWORD_LOCKED_AND_INVALID "!*"

View File

@ -126,11 +126,9 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
}
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
const char *p;
assert(str);
for (p = str; length > 0;) {
for (const char *p = str; length > 0;) {
int encoded_len, r;
char32_t val;
@ -293,14 +291,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool
#endif /* NM_IGNORED */
char *ascii_is_valid(const char *str) {
const char *p;
/* Check whether the string consists of valid ASCII bytes,
* i.e values between 0 and 127, inclusive. */
assert(str);
for (p = str; *p; p++)
for (const char *p = str; *p; p++)
if ((unsigned char) *p >= 128)
return NULL;
@ -322,6 +318,37 @@ char *ascii_is_valid_n(const char *str, size_t len) {
}
#endif /* NM_IGNORED */
int utf8_to_ascii(const char *str, char replacement_char, char **ret) {
/* Convert to a string that has only ASCII chars, replacing anything that is not ASCII
* by replacement_char. */
_cleanup_free_ char *ans = new(char, strlen(str) + 1);
if (!ans)
return -ENOMEM;
char *q = ans;
for (const char *p = str; *p; q++) {
int l;
l = utf8_encoded_valid_unichar(p, SIZE_MAX);
if (l < 0) /* Non-UTF-8, let's not even try to propagate the garbage */
return l;
if (l == 1)
*q = *p;
else
/* non-ASCII, we need to replace it */
*q = replacement_char;
p += l;
}
*q = '\0';
*ret = TAKE_PTR(ans);
return 0;
}
/**
* utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
* @out_utf8: output buffer of at least 4 bytes or NULL

View File

@ -21,6 +21,8 @@ static inline char *utf8_is_valid(const char *s) {
char *ascii_is_valid(const char *s) _pure_;
char *ascii_is_valid_n(const char *str, size_t len);
int utf8_to_ascii(const char *str, char replacement_char, char **ret);
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_;
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)

View File

@ -8,7 +8,6 @@
#include "alloc-util.h"
#include "build.h"
#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
@ -118,71 +117,6 @@ void in_initrd_force(bool value) {
saved_in_initrd = value;
}
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
FOREACH_DIRENT(de, d, return -errno) {
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
if (IN_SET(errno, ENOENT, ENOTDIR))
continue;
return -errno;
}
fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 6 || memcmp(contents, "Mains\n", 6))
continue;
safe_close(fd);
fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 2 || contents[1] != '\n')
return -EIO;
if (contents[0] == '1') {
found_online = true;
break;
} else if (contents[0] == '0')
found_offline = true;
else
return -EIO;
}
return found_online || !found_offline;
}
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;

View File

@ -20,13 +20,20 @@ int prot_from_flags(int flags) _const_;
bool in_initrd(void);
void in_initrd_force(bool value);
int on_ac_power(void);
/* Note: log2(0) == log2(1) == 0 here and below. */
static inline unsigned u64log2(uint64_t n) {
#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
#define NONCONST_LOG2ULL(x) ({ \
unsigned long long _x = (x); \
_x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \
})
#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x))
static inline unsigned log2u64(uint64_t x) {
#if __SIZEOF_LONG_LONG__ == 8
return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
return LOG2ULL(x);
#else
#error "Wut?"
# error "Wut?"
#endif
}
@ -34,26 +41,27 @@ static inline unsigned u32ctz(uint32_t n) {
#if __SIZEOF_INT__ == 4
return n != 0 ? __builtin_ctz(n) : 32;
#else
#error "Wut?"
# error "Wut?"
#endif
}
static inline unsigned log2i(int x) {
assert(x > 0);
#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0)
#define NONCONST_LOG2U(x) ({ \
unsigned _x = (x); \
_x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \
})
#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x))
return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
static inline unsigned log2i(int x) {
return LOG2U(x);
}
static inline unsigned log2u(unsigned x) {
assert(x > 0);
return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
return LOG2U(x);
}
static inline unsigned log2u_round_up(unsigned x) {
assert(x > 0);
if (x == 1)
if (x <= 1)
return 0;
return log2u(x - 1) + 1;

View File

@ -5,13 +5,15 @@
#include <assert.h>
#endif
#include "type.h"
#include <limits.h>
#include "types-fundamental.h"
#define _align_(x) __attribute__((__aligned__(x)))
#define _const_ __attribute__((__const__))
#define _pure_ __attribute__((__pure__))
#define _section_(x) __attribute__((__section__(x)))
#define _packed_ __attribute__((__packed__))
#define _retain_ __attribute__((__retain__))
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x)))
@ -59,8 +61,19 @@
#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__)
#endif
#define memcpy(a, b, c) CopyMem((a), (b), (c))
#define free(a) FreePool(a)
#endif
/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
#define ASSERT_PTR(expr) \
({ \
typeof(expr) _expr_ = (expr); \
assert(_expr_); \
_expr_; \
})
#if defined(static_assert)
#define assert_cc(expr) \
static_assert(expr, #expr)
@ -81,8 +94,8 @@
#define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
#define __ONCE(o) \
({ \
static bool (o) = false; \
__sync_bool_compare_and_swap(&(o), false, true); \
static sd_bool (o) = sd_false; \
__sync_bool_compare_and_swap(&(o), sd_false, sd_true); \
})
#undef MAX
@ -232,7 +245,7 @@
#define IN_SET(x, ...) \
({ \
sd_bool _found = false; \
sd_bool _found = sd_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 \
@ -241,7 +254,7 @@
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
_found = true; \
_found = sd_true; \
break; \
default: \
break; \
@ -253,8 +266,9 @@
* resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
#define TAKE_PTR(ptr) \
({ \
typeof(ptr) _ptr_ = (ptr); \
(ptr) = NULL; \
typeof(ptr) *_pptr_ = &(ptr); \
typeof(ptr) _ptr_ = *_pptr_; \
*_pptr_ = NULL; \
_ptr_; \
})
@ -264,3 +278,47 @@
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
#define mfree(memory) \
({ \
free(memory); \
(typeof(memory)) NULL; \
})
static inline size_t ALIGN_TO(size_t l, size_t ali) {
/* sd-boot uses UINTN for size_t, let's make sure SIZE_MAX is correct. */
assert_cc(SIZE_MAX == ~(size_t)0);
/* Check that alignment is exponent of 2 */
#if SIZE_MAX == UINT_MAX
assert(__builtin_popcount(ali) == 1);
#elif SIZE_MAX == ULONG_MAX
assert(__builtin_popcountl(ali) == 1);
#elif SIZE_MAX == ULLONG_MAX
assert(__builtin_popcountll(ali) == 1);
#else
#error "Unexpected size_t"
#endif
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
return ((l + ali - 1) & ~(ali - 1));
}
/* Same as ALIGN_TO but callable in constant contexts. */
#define CONST_ALIGN_TO(l, ali) \
__builtin_choose_expr( \
__builtin_constant_p(l) && \
__builtin_constant_p(ali) && \
__builtin_popcountll(ali) == 1 && /* is power of 2? */ \
(l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
((l) + (ali) - 1) & ~((ali) - 1), \
VOID_0)
#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)

View File

@ -10,7 +10,7 @@
#include "string-util-fundamental.h"
sd_char *startswith(const sd_char *s, const sd_char *prefix) {
sd_size_t l;
size_t l;
assert(s);
assert(prefix);
@ -24,7 +24,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) {
#ifndef SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
sd_size_t l;
size_t l;
assert(s);
assert(prefix);
@ -38,7 +38,7 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
#endif
sd_char* endswith(const sd_char *s, const sd_char *postfix) {
sd_size_t sl, pl;
size_t sl, pl;
assert(s);
assert(postfix);
@ -59,7 +59,7 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
}
sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
sd_size_t sl, pl;
size_t sl, pl;
assert(s);
assert(postfix);

View File

@ -68,10 +68,10 @@ static inline const sd_char *yes_no(sd_bool b) {
sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) {
assert(token);
sd_size_t n = strlen(token) * sizeof(sd_char);
size_t n = strlen(token) * sizeof(sd_char);
if (sz < n)
return NULL;

View File

@ -1,22 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#ifdef SD_BOOT
#include <efi.h>
typedef BOOLEAN sd_bool;
typedef CHAR16 sd_char;
typedef INTN sd_int;
typedef UINTN sd_size_t;
#define true TRUE
#define false FALSE
#else
#include <stdbool.h>
#include <stdint.h>
typedef bool sd_bool;
typedef char sd_char;
typedef int sd_int;
typedef size_t sd_size_t;
#endif

View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment,
* but mostly the same in concept and behaviour.
*
* Note: if the definition of these types/values has slightly different semantics in userspace and in the
* UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics,
* and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace
* and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI
* space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100%
* defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI
* environment, so that size_t can be used everywhere, without any "sd_" prefix.)
*
* Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types
* after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */
#ifdef SD_BOOT
#include <efi.h>
typedef BOOLEAN sd_bool;
typedef CHAR16 sd_char;
typedef INTN sd_int;
typedef UINTN size_t;
#define sd_true TRUE
#define sd_false FALSE
#else
#include <stdbool.h>
#include <stdint.h>
typedef bool sd_bool;
typedef char sd_char;
typedef int sd_int;
#define sd_true true
#define sd_false false
#endif

View File

@ -687,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
}
/* Found it! Now generate the new name */
prefix = strndupa(name, saved_before - name);
prefix = strndupa_safe(name, saved_before - name);
r = dns_name_concat(prefix, new_suffix, 0, ret);
if (r < 0)
@ -1037,7 +1037,7 @@ static bool dns_service_name_label_is_valid(const char *label, size_t n) {
if (memchr(label, 0, n))
return false;
s = strndupa(label, n);
s = strndupa_safe(label, n);
return dns_service_name_is_valid(s);
}

View File

@ -62,6 +62,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;