mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-26 00:40:08 +01:00
systemd: update code from upstream (2021-10-06)
This is a direct dump from systemd git.
======
SYSTEMD_DIR=../systemd
COMMIT=e5f093bf99ed742b74b81ac97e0e5e01cde02d95
(
cd "$SYSTEMD_DIR"
git checkout "$COMMIT"
git reset --hard
git clean -fdx
)
git ls-files -z :/src/libnm-systemd-core/src/ \
:/src/libnm-systemd-shared/src/ \
:/src/libnm-std-aux/unaligned.h | \
xargs -0 rm -f
nm_copy_sd_shared() {
mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")"
cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1"
}
nm_copy_sd_core() {
mkdir -p "./src/libnm-systemd-core/$(dirname "$1")"
cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1"
}
nm_copy_sd_stdaux() {
mkdir -p "./src/libnm-std-aux/"
cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}"
}
nm_copy_sd_core "src/libsystemd-network/arp-util.c"
nm_copy_sd_core "src/libsystemd-network/arp-util.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp-network.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-option.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c"
nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h"
nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c"
nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c"
nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h"
nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c"
nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h"
nm_copy_sd_core "src/libsystemd-network/lldp-network.c"
nm_copy_sd_core "src/libsystemd-network/lldp-network.h"
nm_copy_sd_core "src/libsystemd-network/lldp-rx-internal.h"
nm_copy_sd_core "src/libsystemd-network/network-common.c"
nm_copy_sd_core "src/libsystemd-network/network-common.h"
nm_copy_sd_core "src/libsystemd-network/network-internal.c"
nm_copy_sd_core "src/libsystemd-network/network-internal.h"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c"
nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c"
nm_copy_sd_core "src/libsystemd-network/sd-ipv4acd.c"
nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c"
nm_copy_sd_core "src/libsystemd-network/sd-lldp-rx.c"
nm_copy_sd_core "src/libsystemd/sd-event/event-source.h"
nm_copy_sd_core "src/libsystemd/sd-event/event-util.c"
nm_copy_sd_core "src/libsystemd/sd-event/event-util.h"
nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c"
nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c"
nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h"
nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c"
nm_copy_sd_core "src/systemd/_sd-common.h"
nm_copy_sd_core "src/systemd/sd-dhcp-client.h"
nm_copy_sd_core "src/systemd/sd-dhcp-lease.h"
nm_copy_sd_core "src/systemd/sd-dhcp-option.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-client.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h"
nm_copy_sd_core "src/systemd/sd-dhcp6-option.h"
nm_copy_sd_core "src/systemd/sd-event.h"
nm_copy_sd_core "src/systemd/sd-id128.h"
nm_copy_sd_core "src/systemd/sd-ipv4acd.h"
nm_copy_sd_core "src/systemd/sd-ipv4ll.h"
nm_copy_sd_core "src/systemd/sd-lldp-rx.h"
nm_copy_sd_core "src/systemd/sd-lldp.h"
nm_copy_sd_core "src/systemd/sd-ndisc.h"
nm_copy_sd_shared "src/basic/alloc-util.c"
nm_copy_sd_shared "src/basic/alloc-util.h"
nm_copy_sd_shared "src/basic/async.h"
nm_copy_sd_shared "src/basic/cgroup-util.h"
nm_copy_sd_shared "src/basic/dns-def.h"
nm_copy_sd_shared "src/basic/env-file.c"
nm_copy_sd_shared "src/basic/env-file.h"
nm_copy_sd_shared "src/basic/env-util.c"
nm_copy_sd_shared "src/basic/env-util.h"
nm_copy_sd_shared "src/basic/errno-util.h"
nm_copy_sd_shared "src/basic/escape.c"
nm_copy_sd_shared "src/basic/escape.h"
nm_copy_sd_shared "src/basic/ether-addr-util.c"
nm_copy_sd_shared "src/basic/ether-addr-util.h"
nm_copy_sd_shared "src/basic/extract-word.c"
nm_copy_sd_shared "src/basic/extract-word.h"
nm_copy_sd_shared "src/basic/fd-util.c"
nm_copy_sd_shared "src/basic/fd-util.h"
nm_copy_sd_shared "src/basic/fileio.c"
nm_copy_sd_shared "src/basic/fileio.h"
nm_copy_sd_shared "src/basic/format-util.c"
nm_copy_sd_shared "src/basic/format-util.h"
nm_copy_sd_shared "src/basic/fs-util.c"
nm_copy_sd_shared "src/basic/fs-util.h"
nm_copy_sd_shared "src/basic/hash-funcs.c"
nm_copy_sd_shared "src/basic/hash-funcs.h"
nm_copy_sd_shared "src/basic/hashmap.c"
nm_copy_sd_shared "src/basic/hashmap.h"
nm_copy_sd_shared "src/basic/hexdecoct.c"
nm_copy_sd_shared "src/basic/hexdecoct.h"
nm_copy_sd_shared "src/basic/hostname-util.c"
nm_copy_sd_shared "src/basic/hostname-util.h"
nm_copy_sd_shared "src/basic/in-addr-util.c"
nm_copy_sd_shared "src/basic/in-addr-util.h"
nm_copy_sd_shared "src/basic/inotify-util.c"
nm_copy_sd_shared "src/basic/inotify-util.h"
nm_copy_sd_shared "src/basic/io-util.c"
nm_copy_sd_shared "src/basic/io-util.h"
nm_copy_sd_shared "src/basic/list.h"
nm_copy_sd_shared "src/basic/log.h"
nm_copy_sd_shared "src/basic/macro.h"
nm_copy_sd_shared "src/basic/memory-util.c"
nm_copy_sd_shared "src/basic/memory-util.h"
nm_copy_sd_shared "src/basic/mempool.c"
nm_copy_sd_shared "src/basic/mempool.h"
nm_copy_sd_shared "src/basic/missing_fcntl.h"
nm_copy_sd_shared "src/basic/missing_random.h"
nm_copy_sd_shared "src/basic/missing_socket.h"
nm_copy_sd_shared "src/basic/missing_stat.h"
nm_copy_sd_shared "src/basic/missing_syscall.h"
nm_copy_sd_shared "src/basic/missing_type.h"
nm_copy_sd_shared "src/basic/ordered-set.c"
nm_copy_sd_shared "src/basic/ordered-set.h"
nm_copy_sd_shared "src/basic/parse-util.c"
nm_copy_sd_shared "src/basic/parse-util.h"
nm_copy_sd_shared "src/basic/path-util.c"
nm_copy_sd_shared "src/basic/path-util.h"
nm_copy_sd_shared "src/basic/prioq.c"
nm_copy_sd_shared "src/basic/prioq.h"
nm_copy_sd_shared "src/basic/process-util.c"
nm_copy_sd_shared "src/basic/process-util.h"
nm_copy_sd_shared "src/basic/random-util.c"
nm_copy_sd_shared "src/basic/random-util.h"
nm_copy_sd_shared "src/basic/ratelimit.c"
nm_copy_sd_shared "src/basic/ratelimit.h"
nm_copy_sd_shared "src/basic/set.h"
nm_copy_sd_shared "src/basic/signal-util.c"
nm_copy_sd_shared "src/basic/signal-util.h"
nm_copy_sd_shared "src/basic/siphash24.h"
nm_copy_sd_shared "src/basic/socket-util.c"
nm_copy_sd_shared "src/basic/socket-util.h"
nm_copy_sd_shared "src/basic/sort-util.h"
nm_copy_sd_shared "src/basic/sparse-endian.h"
nm_copy_sd_shared "src/basic/stat-util.c"
nm_copy_sd_shared "src/basic/stat-util.h"
nm_copy_sd_shared "src/basic/stdio-util.h"
nm_copy_sd_shared "src/basic/string-table.c"
nm_copy_sd_shared "src/basic/string-table.h"
nm_copy_sd_shared "src/basic/string-util.c"
nm_copy_sd_shared "src/basic/string-util.h"
nm_copy_sd_shared "src/basic/strv.c"
nm_copy_sd_shared "src/basic/strv.h"
nm_copy_sd_shared "src/basic/strxcpyx.c"
nm_copy_sd_shared "src/basic/strxcpyx.h"
nm_copy_sd_shared "src/basic/time-util.c"
nm_copy_sd_shared "src/basic/time-util.h"
nm_copy_sd_shared "src/basic/tmpfile-util.c"
nm_copy_sd_shared "src/basic/tmpfile-util.h"
nm_copy_sd_shared "src/basic/umask-util.h"
nm_copy_sd_shared "src/basic/user-util.h"
nm_copy_sd_shared "src/basic/utf8.c"
nm_copy_sd_shared "src/basic/utf8.h"
nm_copy_sd_shared "src/basic/util.c"
nm_copy_sd_shared "src/basic/util.h"
nm_copy_sd_shared "src/fundamental/macro-fundamental.h"
nm_copy_sd_shared "src/fundamental/string-util-fundamental.c"
nm_copy_sd_shared "src/fundamental/string-util-fundamental.h"
nm_copy_sd_shared "src/fundamental/type.h"
nm_copy_sd_shared "src/shared/dns-domain.c"
nm_copy_sd_shared "src/shared/dns-domain.h"
nm_copy_sd_shared "src/shared/log-link.h"
nm_copy_sd_shared "src/shared/web-util.c"
nm_copy_sd_shared "src/shared/web-util.h"
nm_copy_sd_stdaux "src/basic/unaligned.h"
This commit is contained in:
parent
08ee0a83a1
commit
51f93e00a2
80 changed files with 2208 additions and 2546 deletions
|
|
@ -12,7 +12,7 @@
|
|||
#include "sd-dhcp-client.h"
|
||||
|
||||
#include "dhcp-protocol.h"
|
||||
#include "log-link.h"
|
||||
#include "network-common.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
typedef struct sd_dhcp_option {
|
||||
|
|
@ -75,10 +75,10 @@ void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode);
|
|||
#define log_dhcp_client_errno(client, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"DHCPv4 client: ", \
|
||||
sd_dhcp_client_get_ifname(client), \
|
||||
sd_dhcp_client, client, \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp_client(client, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"DHCPv4 client: ", \
|
||||
sd_dhcp_client_get_ifname(client), \
|
||||
sd_dhcp_client, client, \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
#include "log-link.h"
|
||||
#include "macro.h"
|
||||
#include "network-common.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
typedef struct sd_dhcp6_option {
|
||||
|
|
@ -101,15 +101,29 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
|||
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
|
||||
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
||||
size_t *optlen, uint8_t **optvalue);
|
||||
int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
|
||||
int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count);
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
|
||||
char ***str_arr);
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str);
|
||||
|
||||
int dhcp6_option_parse(
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
size_t *offset,
|
||||
uint16_t *ret_option_code,
|
||||
size_t *ret_option_data_len,
|
||||
const uint8_t **ret_option_data);
|
||||
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
|
||||
int dhcp6_option_parse_ia(
|
||||
sd_dhcp6_client *client,
|
||||
be32_t iaid,
|
||||
uint16_t option_code,
|
||||
size_t option_data_len,
|
||||
const uint8_t *option_data,
|
||||
DHCP6IA *ret);
|
||||
int dhcp6_option_parse_addresses(
|
||||
const uint8_t *optval,
|
||||
size_t optlen,
|
||||
struct in6_addr **addrs,
|
||||
size_t *count);
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
|
||||
|
||||
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
|
||||
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
|
||||
|
|
@ -125,10 +139,10 @@ void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
|
|||
#define log_dhcp6_client_errno(client, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"DHCPv6 client: ", \
|
||||
sd_dhcp6_client_get_ifname(client), \
|
||||
sd_dhcp6_client, client, \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_dhcp6_client(client, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"DHCPv6 client: ", \
|
||||
sd_dhcp6_client_get_ifname(client), \
|
||||
sd_dhcp6_client, client, \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ struct sd_dhcp6_lease {
|
|||
struct in6_addr *dns;
|
||||
size_t dns_count;
|
||||
char **domains;
|
||||
size_t domains_count;
|
||||
struct in6_addr *ntp;
|
||||
size_t ntp_count;
|
||||
char **ntp_fqdn;
|
||||
size_t ntp_fqdn_count;
|
||||
struct in6_addr *sntp;
|
||||
size_t sntp_count;
|
||||
char *fqdn;
|
||||
};
|
||||
|
||||
|
|
@ -50,12 +50,10 @@ 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_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
|
||||
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
|
||||
size_t optlen);
|
||||
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
|
||||
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
|
||||
size_t optlen) ;
|
||||
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_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
|
||||
|
||||
int dhcp6_lease_new(sd_dhcp6_lease **ret);
|
||||
|
|
|
|||
|
|
@ -14,29 +14,12 @@
|
|||
#include "dhcp6-lease-internal.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "escape.h"
|
||||
#include "memory-util.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "strv.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
typedef struct DHCP6StatusOption {
|
||||
struct DHCP6Option option;
|
||||
be16_t status;
|
||||
char msg[];
|
||||
} _packed_ DHCP6StatusOption;
|
||||
|
||||
typedef struct DHCP6AddressOption {
|
||||
struct DHCP6Option option;
|
||||
struct iaaddr iaaddr;
|
||||
uint8_t options[];
|
||||
} _packed_ DHCP6AddressOption;
|
||||
|
||||
typedef struct DHCP6PDPrefixOption {
|
||||
struct DHCP6Option option;
|
||||
struct iapdprefix iapdprefix;
|
||||
uint8_t options[];
|
||||
} _packed_ DHCP6PDPrefixOption;
|
||||
|
||||
#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
|
||||
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
|
||||
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
|
||||
|
|
@ -370,337 +353,410 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const
|
|||
return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
|
||||
}
|
||||
|
||||
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
|
||||
DHCP6Option *option = (DHCP6Option*) *buf;
|
||||
uint16_t len;
|
||||
int dhcp6_option_parse(
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
size_t *offset,
|
||||
uint16_t *ret_option_code,
|
||||
size_t *ret_option_data_len,
|
||||
const uint8_t **ret_option_data) {
|
||||
|
||||
assert_return(buf, -EINVAL);
|
||||
assert_return(optcode, -EINVAL);
|
||||
assert_return(optlen, -EINVAL);
|
||||
const DHCP6Option *option;
|
||||
size_t len;
|
||||
|
||||
if (*buflen < offsetof(DHCP6Option, data))
|
||||
return -ENOMSG;
|
||||
assert(buf);
|
||||
assert(offset);
|
||||
assert(ret_option_code);
|
||||
assert(ret_option_data_len);
|
||||
assert(ret_option_data);
|
||||
|
||||
if (buflen < offsetof(DHCP6Option, data))
|
||||
return -EBADMSG;
|
||||
|
||||
if (*offset >= buflen - offsetof(DHCP6Option, data))
|
||||
return -EBADMSG;
|
||||
|
||||
option = (const DHCP6Option*) (buf + *offset);
|
||||
len = be16toh(option->len);
|
||||
|
||||
if (len > *buflen)
|
||||
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
|
||||
return -EBADMSG;
|
||||
|
||||
*offset += offsetof(DHCP6Option, data) + len;
|
||||
*ret_option_code = be16toh(option->code);
|
||||
*ret_option_data_len = len;
|
||||
*ret_option_data = option->data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
|
||||
assert(data);
|
||||
|
||||
if (data_len < sizeof(uint16_t))
|
||||
return -EBADMSG;
|
||||
|
||||
if (ret_status_message) {
|
||||
char *msg;
|
||||
|
||||
/* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
|
||||
* Let's escape unsafe characters for safety. */
|
||||
msg = cescape_length((const char*) (data + sizeof(uint16_t)), data_len - sizeof(uint16_t));
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_status_message = msg;
|
||||
}
|
||||
|
||||
return unaligned_read_be16(data);
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
|
||||
int r;
|
||||
|
||||
assert(buf);
|
||||
|
||||
for(size_t offset = 0; offset < buflen;) {
|
||||
const uint8_t *data;
|
||||
size_t data_len;
|
||||
uint16_t code;
|
||||
|
||||
r = dhcp6_option_parse(buf, buflen, &offset, &code, &data_len, &data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch(code) {
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
_cleanup_free_ char *msg = NULL;
|
||||
|
||||
r = dhcp6_option_parse_status(data, data_len, &msg);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
/* Let's log but ignore the invalid status option. */
|
||||
log_dhcp6_client_errno(client, r,
|
||||
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
|
||||
else if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_dhcp6_client(client, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
|
||||
uint32_t lt_valid, lt_pref;
|
||||
DHCP6Address *a;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(ret);
|
||||
|
||||
if (len < sizeof(struct iaaddr))
|
||||
return -EBADMSG;
|
||||
|
||||
lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid);
|
||||
lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA address with zero valid lifetime, ignoring.");
|
||||
if (lt_pref > lt_valid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA address with preferred lifetime %"PRIu32
|
||||
" larger than valid lifetime %"PRIu32", ignoring.",
|
||||
lt_pref, lt_valid);
|
||||
|
||||
if (len > sizeof(struct iaaddr)) {
|
||||
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, a);
|
||||
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
|
||||
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
|
||||
uint32_t lt_valid, lt_pref;
|
||||
DHCP6Address *a;
|
||||
int r;
|
||||
|
||||
if (len < sizeof(struct iapdprefix))
|
||||
return -ENOMSG;
|
||||
|
||||
*optcode = be16toh(option->code);
|
||||
*optlen = len;
|
||||
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
|
||||
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
|
||||
|
||||
*buf += 4;
|
||||
*buflen -= 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
||||
size_t *optlen, uint8_t **optvalue) {
|
||||
int r;
|
||||
|
||||
assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
|
||||
|
||||
r = option_parse_hdr(buf, buflen, optcode, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (*optlen > *buflen)
|
||||
return -ENOBUFS;
|
||||
|
||||
*optvalue = *buf;
|
||||
*buflen -= *optlen;
|
||||
*buf += *optlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
|
||||
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
|
||||
|
||||
if (len < sizeof(DHCP6StatusOption) ||
|
||||
be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
|
||||
return -ENOBUFS;
|
||||
|
||||
return be16toh(statusopt->status);
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
|
||||
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
|
||||
DHCP6Address *addr;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
|
||||
return -ENOBUFS;
|
||||
|
||||
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0 || lt_pref > lt_valid)
|
||||
if (lt_valid == 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Valid lifetime of an IA address is zero or "
|
||||
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
|
||||
"Received a PD prefix with zero valid lifetime, ignoring.");
|
||||
if (lt_pref > lt_valid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received a PD prefix with preferred lifetime %"PRIu32
|
||||
" larger than valid lifetime %"PRIu32", ignoring.",
|
||||
lt_pref, lt_valid);
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
|
||||
if (len > sizeof(struct iapdprefix)) {
|
||||
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Non-zero status code '%s' for address is received",
|
||||
dhcp6_message_status_to_string(r));
|
||||
}
|
||||
|
||||
addr = new0(DHCP6Address, 1);
|
||||
if (!addr)
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, addr);
|
||||
memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
|
||||
|
||||
LIST_PREPEND(addresses, ia->addresses, addr);
|
||||
|
||||
*ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
|
||||
LIST_INIT(addresses, a);
|
||||
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
|
||||
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
|
||||
DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
|
||||
DHCP6Address *prefix;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int dhcp6_option_parse_ia(
|
||||
sd_dhcp6_client *client,
|
||||
be32_t iaid,
|
||||
uint16_t option_code,
|
||||
size_t option_data_len,
|
||||
const uint8_t *option_data,
|
||||
DHCP6IA *ret) {
|
||||
|
||||
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||
uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
|
||||
be32_t received_iaid;
|
||||
size_t offset;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
|
||||
return -ENOBUFS;
|
||||
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
|
||||
assert(option_data);
|
||||
assert(ret);
|
||||
|
||||
lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
|
||||
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
|
||||
/* This will return the following:
|
||||
* -ENOMEM: memory allocation error,
|
||||
* -ENOANO: unmatching IAID,
|
||||
* -EINVAL: non-zero status code, or invalid lifetime,
|
||||
* -EBADMSG: invalid message format,
|
||||
* -ENODATA: no valid address or PD prefix,
|
||||
* 0: success. */
|
||||
|
||||
if (lt_valid == 0 || lt_pref > lt_valid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Valid lifetieme of a PD prefix is zero or "
|
||||
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
|
||||
lt_pref, lt_valid);
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Non-zero status code '%s' for PD prefix is received",
|
||||
dhcp6_message_status_to_string(r));
|
||||
}
|
||||
|
||||
prefix = new0(DHCP6Address, 1);
|
||||
if (!prefix)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, prefix);
|
||||
memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
|
||||
|
||||
LIST_PREPEND(addresses, ia->addresses, prefix);
|
||||
|
||||
*ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
|
||||
uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
|
||||
uint16_t iatype, optlen;
|
||||
size_t iaaddr_offset;
|
||||
int r = 0, status;
|
||||
size_t i, len;
|
||||
uint16_t opt;
|
||||
|
||||
assert_return(ia, -EINVAL);
|
||||
assert_return(!ia->addresses, -EINVAL);
|
||||
|
||||
iatype = be16toh(iaoption->code);
|
||||
len = be16toh(iaoption->len);
|
||||
|
||||
switch (iatype) {
|
||||
switch (option_code) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
|
||||
if (len < DHCP6_OPTION_IA_NA_LEN)
|
||||
return -ENOBUFS;
|
||||
if (option_data_len < DHCP6_OPTION_IA_NA_LEN)
|
||||
return -EBADMSG;
|
||||
|
||||
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
|
||||
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
|
||||
|
||||
lt_t1 = be32toh(ia->ia_na.lifetime_t1);
|
||||
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
|
||||
|
||||
if (lt_t1 > lt_t2)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec",
|
||||
lt_t1, lt_t2);
|
||||
offset = DHCP6_OPTION_IA_NA_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_na*) option_data)->id;
|
||||
lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1);
|
||||
lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
|
||||
if (len < sizeof(ia->ia_pd))
|
||||
return -ENOBUFS;
|
||||
if (option_data_len < DHCP6_OPTION_IA_PD_LEN)
|
||||
return -EBADMSG;
|
||||
|
||||
iaaddr_offset = sizeof(ia->ia_pd);
|
||||
memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
|
||||
|
||||
lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
|
||||
lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
|
||||
|
||||
if (lt_t1 > lt_t2)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec",
|
||||
lt_t1, lt_t2);
|
||||
offset = DHCP6_OPTION_IA_PD_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_pd*) option_data)->id;
|
||||
lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1);
|
||||
lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
if (len < DHCP6_OPTION_IA_TA_LEN)
|
||||
return -ENOBUFS;
|
||||
if (option_data_len < DHCP6_OPTION_IA_TA_LEN)
|
||||
return -ENOMSG;
|
||||
|
||||
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
|
||||
memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
|
||||
offset = DHCP6_OPTION_IA_TA_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_ta*) option_data)->id;
|
||||
lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOMSG;
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
ia->type = iatype;
|
||||
i = iaaddr_offset;
|
||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||
* but not necessary to ignore or refuse the whole message. */
|
||||
if (received_iaid != iaid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
|
||||
"Received an IA option with a different IAID "
|
||||
"from the one chosen by the client, ignoring.");
|
||||
|
||||
while (i < len) {
|
||||
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
|
||||
if (lt_t1 > lt_t2)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
|
||||
lt_t1, lt_t2);
|
||||
|
||||
if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
|
||||
return -ENOBUFS;
|
||||
for (; offset < option_data_len;) {
|
||||
const uint8_t *subdata;
|
||||
size_t subdata_len;
|
||||
uint16_t subopt;
|
||||
|
||||
opt = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (opt) {
|
||||
case SD_DHCP6_OPTION_IAADDR:
|
||||
switch (subopt) {
|
||||
case SD_DHCP6_OPTION_IAADDR: {
|
||||
DHCP6Address *a;
|
||||
|
||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA Address option not in IA NA or TA option");
|
||||
|
||||
r = dhcp6_option_parse_address(client, option, ia, <_valid);
|
||||
if (r < 0 && r != -EINVAL)
|
||||
return r;
|
||||
if (r >= 0 && lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX:
|
||||
|
||||
if (ia->type != SD_DHCP6_OPTION_IA_PD)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA PD Prefix option not in IA PD option");
|
||||
|
||||
r = dhcp6_option_parse_pdprefix(client, option, ia, <_valid);
|
||||
if (r < 0 && r != -EINVAL)
|
||||
return r;
|
||||
if (r >= 0 && lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
|
||||
status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (status > 0) {
|
||||
if (ret_status_code)
|
||||
*ret_status_code = status;
|
||||
|
||||
log_dhcp6_client(client, "IA status %s",
|
||||
dhcp6_message_status_to_string(status));
|
||||
|
||||
return 0;
|
||||
if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
|
||||
log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
/* Ignore the sub-option on non-critical errors. */
|
||||
continue;
|
||||
|
||||
lt_min = MIN(lt_min, a->iaaddr.lifetime_valid);
|
||||
LIST_PREPEND(addresses, ia.addresses, a);
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
|
||||
DHCP6Address *a;
|
||||
|
||||
if (option_code != SD_DHCP6_OPTION_IA_PD) {
|
||||
log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
/* Ignore the sub-option on non-critical errors. */
|
||||
continue;
|
||||
|
||||
lt_min = MIN(lt_min, a->iapdprefix.lifetime_valid);
|
||||
LIST_PREPEND(addresses, ia.addresses, a);
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
_cleanup_free_ char *msg = NULL;
|
||||
|
||||
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r,
|
||||
"Received an IA option with an invalid status sub option, ignoring: %m");
|
||||
else if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA option with non-zero status: %s%s%s",
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_dhcp6_client(client, "Unknown IA option %d", opt);
|
||||
break;
|
||||
log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt);
|
||||
}
|
||||
|
||||
i += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
switch(iatype) {
|
||||
if (!ia.addresses)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
|
||||
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
|
||||
|
||||
if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
|
||||
lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
|
||||
log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
|
||||
"Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
|
||||
"T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
switch(option_code) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
if (ia->ia_na.lifetime_t1 == 0 && ia->ia_na.lifetime_t2 == 0 && lt_min != UINT32_MAX) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
ia->ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
ia->ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
|
||||
log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
|
||||
lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
*ret = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
.ia_na.id = iaid,
|
||||
.ia_na.lifetime_t1 = htobe32(lt_t1),
|
||||
.ia_na.lifetime_t2 = htobe32(lt_t2),
|
||||
.addresses = TAKE_PTR(ia.addresses),
|
||||
};
|
||||
break;
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
*ret = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
.ia_ta.id = iaid,
|
||||
.addresses = TAKE_PTR(ia.addresses),
|
||||
};
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
if (ia->ia_pd.lifetime_t1 == 0 && ia->ia_pd.lifetime_t2 == 0 && lt_min != UINT32_MAX) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
|
||||
ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
|
||||
|
||||
log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
|
||||
lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
*ret = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
.ia_pd.id = iaid,
|
||||
.ia_pd.lifetime_t1 = htobe32(lt_t1),
|
||||
.ia_pd.lifetime_t2 = htobe32(lt_t2),
|
||||
.addresses = TAKE_PTR(ia.addresses),
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (ret_status_code)
|
||||
*ret_status_code = 0;
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count) {
|
||||
int dhcp6_option_parse_addresses(
|
||||
const uint8_t *optval,
|
||||
size_t optlen,
|
||||
struct in6_addr **addrs,
|
||||
size_t *count) {
|
||||
|
||||
assert(optval);
|
||||
assert(addrs);
|
||||
assert(count);
|
||||
|
||||
if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
|
||||
return -EINVAL;
|
||||
return -EBADMSG;
|
||||
|
||||
if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen))
|
||||
if (!GREEDY_REALLOC(*addrs, *count + optlen / sizeof(struct in6_addr)))
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(*addrs + count, optval, optlen);
|
||||
memcpy(*addrs + *count, optval, optlen);
|
||||
*count += optlen / sizeof(struct in6_addr);
|
||||
|
||||
count += optlen / sizeof(struct in6_addr);
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
const uint8_t *optval = *data;
|
||||
uint16_t optlen = *len;
|
||||
bool first = true;
|
||||
size_t n = 0;
|
||||
static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
|
||||
_cleanup_free_ char *domain = NULL;
|
||||
const uint8_t *optval;
|
||||
size_t optlen, n = 0;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(*data);
|
||||
assert(len);
|
||||
assert(ret);
|
||||
|
||||
optval = *data;
|
||||
optlen = *len;
|
||||
|
||||
if (optlen <= 1)
|
||||
return -ENODATA;
|
||||
|
||||
|
|
@ -724,42 +780,44 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain)
|
|||
return -EMSGSIZE;
|
||||
|
||||
/* Literal label */
|
||||
label = (const char *)optval;
|
||||
label = (const char*) optval;
|
||||
optval += c;
|
||||
optlen -= c;
|
||||
|
||||
if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX))
|
||||
if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
|
||||
return -ENOMEM;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret[n++] = '.';
|
||||
if (n != 0)
|
||||
domain[n++] = '.';
|
||||
|
||||
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
|
||||
r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n += r;
|
||||
}
|
||||
|
||||
if (n) {
|
||||
if (!GREEDY_REALLOC(ret, n + 1))
|
||||
if (n > 0) {
|
||||
if (!GREEDY_REALLOC(domain, n + 1))
|
||||
return -ENOMEM;
|
||||
ret[n] = 0;
|
||||
|
||||
domain[n] = '\0';
|
||||
}
|
||||
|
||||
*out_domain = TAKE_PTR(ret);
|
||||
*ret = TAKE_PTR(domain);
|
||||
*data = optval;
|
||||
*len = optlen;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) {
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
|
||||
_cleanup_free_ char *domain = NULL;
|
||||
int r;
|
||||
|
||||
assert(optval);
|
||||
assert(ret);
|
||||
|
||||
r = parse_domain(&optval, &optlen, &domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
@ -768,39 +826,38 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
|
|||
if (optlen != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*str = TAKE_PTR(domain);
|
||||
*ret = TAKE_PTR(domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
|
||||
size_t idx = 0;
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
int r;
|
||||
|
||||
assert(optval);
|
||||
assert(ret);
|
||||
|
||||
if (optlen <= 1)
|
||||
return -ENODATA;
|
||||
if (optval[optlen - 1] != '\0')
|
||||
return -EINVAL;
|
||||
|
||||
while (optlen > 0) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
_cleanup_free_ char *name = NULL;
|
||||
|
||||
r = parse_domain(&optval, &optlen, &ret);
|
||||
r = parse_domain(&optval, &optlen, &name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = strv_extend(&names, ret);
|
||||
r = strv_consume(&names, TAKE_PTR(name));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
*str_arr = TAKE_PTR(names);
|
||||
|
||||
return idx;
|
||||
*ret = TAKE_PTR(names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,15 @@
|
|||
#include "ether-addr-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "lldp-internal.h"
|
||||
#include "lldp-neighbor.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
|
||||
assert(id);
|
||||
assert(state);
|
||||
|
||||
siphash24_compress(id->chassis_id, id->chassis_id_size, state);
|
||||
siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
|
||||
siphash24_compress(id->port_id, id->port_id_size, state);
|
||||
|
|
@ -19,16 +21,27 @@ static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash
|
|||
}
|
||||
|
||||
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
|
||||
?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func,
|
||||
sd_lldp_neighbor, lldp_neighbor_unlink);
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
lldp_neighbor_hash_ops,
|
||||
LLDPNeighborID,
|
||||
lldp_neighbor_id_hash_func,
|
||||
lldp_neighbor_id_compare_func,
|
||||
sd_lldp_neighbor,
|
||||
lldp_neighbor_unlink);
|
||||
|
||||
int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
|
||||
const sd_lldp_neighbor *x = a, *y = b;
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
return CMP(x->until, y->until);
|
||||
}
|
||||
|
||||
|
|
@ -36,14 +49,15 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
|
|||
if (!n)
|
||||
return NULL;
|
||||
|
||||
assert(n->n_ref > 0 || n->lldp);
|
||||
assert(n->n_ref > 0 || n->lldp_rx);
|
||||
n->n_ref++;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void lldp_neighbor_free(sd_lldp_neighbor *n) {
|
||||
assert(n);
|
||||
static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
free(n->id.port_id);
|
||||
free(n->id.chassis_id);
|
||||
|
|
@ -53,7 +67,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
|
|||
free(n->mud_url);
|
||||
free(n->chassis_id_as_string);
|
||||
free(n->port_id_as_string);
|
||||
free(n);
|
||||
return mfree(n);
|
||||
}
|
||||
|
||||
_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
|
||||
|
|
@ -67,7 +81,7 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
|
|||
assert(n->n_ref > 0);
|
||||
n->n_ref--;
|
||||
|
||||
if (n->n_ref <= 0 && !n->lldp)
|
||||
if (n->n_ref <= 0 && !n->lldp_rx)
|
||||
lldp_neighbor_free(n);
|
||||
|
||||
return NULL;
|
||||
|
|
@ -80,18 +94,18 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
|
|||
if (!n)
|
||||
return NULL;
|
||||
|
||||
if (!n->lldp)
|
||||
if (!n->lldp_rx)
|
||||
return NULL;
|
||||
|
||||
/* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
|
||||
* because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
|
||||
* ourselves from the hashtable and sometimes are called after we already are de-registered. */
|
||||
|
||||
(void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
|
||||
(void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n);
|
||||
|
||||
assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
|
||||
assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
|
||||
|
||||
n->lldp = NULL;
|
||||
n->lldp_rx = NULL;
|
||||
|
||||
if (n->n_ref <= 0)
|
||||
lldp_neighbor_free(n);
|
||||
|
|
@ -112,7 +126,7 @@ sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
|
|||
return n;
|
||||
}
|
||||
|
||||
static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
|
||||
static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) {
|
||||
const char *p = q;
|
||||
char *k;
|
||||
|
||||
|
|
@ -120,7 +134,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
|
|||
assert(p || n == 0);
|
||||
|
||||
if (*s) {
|
||||
log_lldp(lldp, "Found duplicate string, ignoring field.");
|
||||
log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +147,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
|
|||
|
||||
/* Look for inner NULs */
|
||||
if (memchr(p, 0, n)) {
|
||||
log_lldp(lldp, "Found inner NUL in string, ignoring field.");
|
||||
log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -157,14 +171,14 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
assert(n);
|
||||
|
||||
if (n->raw_size < sizeof(struct ether_header))
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received truncated packet, ignoring.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received truncated packet, ignoring.");
|
||||
|
||||
memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
|
||||
|
||||
if (h.ether_type != htobe16(ETHERTYPE_LLDP))
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received packet with wrong type, ignoring.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received packet with wrong type, ignoring.");
|
||||
|
||||
if (h.ether_dhost[0] != 0x01 ||
|
||||
h.ether_dhost[1] != 0x80 ||
|
||||
|
|
@ -172,8 +186,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
h.ether_dhost[3] != 0x00 ||
|
||||
h.ether_dhost[4] != 0x00 ||
|
||||
!IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e))
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received packet with wrong destination address, ignoring.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received packet with wrong destination address, ignoring.");
|
||||
|
||||
memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
|
||||
memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
|
||||
|
|
@ -186,7 +200,7 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
uint16_t length;
|
||||
|
||||
if (left < 2)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"TLV lacks header, ignoring.");
|
||||
|
||||
type = p[0] >> 1;
|
||||
|
|
@ -194,15 +208,15 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
p += 2, left -= 2;
|
||||
|
||||
if (left < length)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"TLV truncated, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"TLV truncated, ignoring datagram.");
|
||||
|
||||
switch (type) {
|
||||
|
||||
case SD_LLDP_TYPE_END:
|
||||
if (length != 0)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"End marker TLV not zero-sized, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"End marker TLV not zero-sized, ignoring datagram.");
|
||||
|
||||
/* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
|
||||
* as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
|
||||
|
|
@ -212,12 +226,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
case SD_LLDP_TYPE_CHASSIS_ID:
|
||||
if (length < 2 || length > 256)
|
||||
/* includes the chassis subtype, hence one extra byte */
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Chassis ID field size out of range, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Chassis ID field size out of range, ignoring datagram.");
|
||||
|
||||
if (n->id.chassis_id)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate chassis ID field, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate chassis ID field, ignoring datagram.");
|
||||
|
||||
n->id.chassis_id = memdup(p, length);
|
||||
if (!n->id.chassis_id)
|
||||
|
|
@ -229,12 +243,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
case SD_LLDP_TYPE_PORT_ID:
|
||||
if (length < 2 || length > 256)
|
||||
/* includes the port subtype, hence one extra byte */
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Port ID field size out of range, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Port ID field size out of range, ignoring datagram.");
|
||||
|
||||
if (n->id.port_id)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate port ID field, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate port ID field, ignoring datagram.");
|
||||
|
||||
n->id.port_id = memdup(p, length);
|
||||
if (!n->id.port_id)
|
||||
|
|
@ -245,39 +259,39 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
|
||||
case SD_LLDP_TYPE_TTL:
|
||||
if (length != 2)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"TTL field has wrong size, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"TTL field has wrong size, ignoring datagram.");
|
||||
|
||||
if (n->has_ttl)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate TTL field, ignoring datagram.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Duplicate TTL field, ignoring datagram.");
|
||||
|
||||
n->ttl = unaligned_read_be16(p);
|
||||
n->has_ttl = true;
|
||||
break;
|
||||
|
||||
case SD_LLDP_TYPE_PORT_DESCRIPTION:
|
||||
r = parse_string(n->lldp, &n->port_description, p, length);
|
||||
r = parse_string(n->lldp_rx, &n->port_description, p, length);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case SD_LLDP_TYPE_SYSTEM_NAME:
|
||||
r = parse_string(n->lldp, &n->system_name, p, length);
|
||||
r = parse_string(n->lldp_rx, &n->system_name, p, length);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
|
||||
r = parse_string(n->lldp, &n->system_description, p, length);
|
||||
r = parse_string(n->lldp_rx, &n->system_description, p, length);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
|
||||
if (length != 4)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"System capabilities field has wrong size.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"System capabilities field has wrong size.");
|
||||
|
||||
n->system_capabilities = unaligned_read_be16(p);
|
||||
n->enabled_capabilities = unaligned_read_be16(p + 2);
|
||||
|
|
@ -286,14 +300,13 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
|
||||
case SD_LLDP_TYPE_PRIVATE:
|
||||
if (length < 4)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Found private TLV that is too short, ignoring.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Found private TLV that is too short, ignoring.");
|
||||
|
||||
/* RFC 8520: MUD URL */
|
||||
if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
|
||||
p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
|
||||
r = parse_string(n->lldp, &n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
|
||||
length - 1 - sizeof(SD_LLDP_OUI_MUD));
|
||||
if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) {
|
||||
r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD),
|
||||
length - sizeof(SD_LLDP_OUI_IANA_MUD));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
|
@ -305,8 +318,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
|
|||
|
||||
end_marker:
|
||||
if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl)
|
||||
return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"One or more mandatory TLV missing in datagram. Ignoring.");
|
||||
return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"One or more mandatory TLV missing in datagram. Ignoring.");
|
||||
|
||||
n->rindex = sizeof(struct ether_header);
|
||||
|
||||
|
|
@ -328,8 +341,8 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
|
|||
} else
|
||||
n->until = 0;
|
||||
|
||||
if (n->lldp)
|
||||
prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
|
||||
if (n->lldp_rx)
|
||||
prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
|
||||
}
|
||||
|
||||
bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-lldp.h"
|
||||
#include "sd-lldp-rx.h"
|
||||
|
||||
#include "hash-funcs.h"
|
||||
#include "lldp-internal.h"
|
||||
#include "lldp-rx-internal.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct LLDPNeighborID {
|
||||
|
|
@ -21,8 +21,8 @@ typedef struct LLDPNeighborID {
|
|||
} LLDPNeighborID;
|
||||
|
||||
struct sd_lldp_neighbor {
|
||||
/* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */
|
||||
sd_lldp *lldp;
|
||||
/* Neighbor objects stay around as long as they are linked into an "sd_lldp_rx" object or n_ref > 0. */
|
||||
sd_lldp_rx *lldp_rx;
|
||||
unsigned n_ref;
|
||||
|
||||
triple_timestamp timestamp;
|
||||
|
|
|
|||
|
|
@ -56,15 +56,18 @@ int lldp_network_bind_raw_socket(int ifindex) {
|
|||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
/* customer bridge */
|
||||
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||
if (r < 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)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "sd-event.h"
|
||||
#include "sd-lldp.h"
|
||||
#include "sd-lldp-rx.h"
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "log-link.h"
|
||||
#include "network-common.h"
|
||||
#include "prioq.h"
|
||||
|
||||
struct sd_lldp {
|
||||
struct sd_lldp_rx {
|
||||
unsigned n_ref;
|
||||
|
||||
int ifindex;
|
||||
|
|
@ -25,7 +25,7 @@ struct sd_lldp {
|
|||
|
||||
uint64_t neighbors_max;
|
||||
|
||||
sd_lldp_callback_t callback;
|
||||
sd_lldp_rx_callback_t callback;
|
||||
void *userdata;
|
||||
|
||||
uint16_t capability_mask;
|
||||
|
|
@ -33,16 +33,16 @@ struct sd_lldp {
|
|||
struct ether_addr filter_address;
|
||||
};
|
||||
|
||||
const char* lldp_event_to_string(sd_lldp_event_t e) _const_;
|
||||
sd_lldp_event_t lldp_event_from_string(const char *s) _pure_;
|
||||
const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_;
|
||||
sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_;
|
||||
|
||||
#define log_lldp_errno(lldp, error, fmt, ...) \
|
||||
#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"LLDP: ", \
|
||||
sd_lldp_get_ifname(lldp), \
|
||||
"LLDP Rx: ", \
|
||||
sd_lldp_rx, lldp_rx, \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_lldp(lldp, fmt, ...) \
|
||||
#define log_lldp_rx(lldp_rx, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"LLDP: ", \
|
||||
sd_lldp_get_ifname(lldp), \
|
||||
"LLDP Rx: ", \
|
||||
sd_lldp_rx, lldp_rx, \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
|
@ -2,23 +2,14 @@
|
|||
|
||||
#include "format-util.h"
|
||||
#include "network-common.h"
|
||||
#include "string-util.h"
|
||||
|
||||
const char *get_ifname(int ifindex, char **ifname) {
|
||||
char buf[IF_NAMESIZE + 1];
|
||||
|
||||
int get_ifname(int ifindex, char **ifname) {
|
||||
assert(ifname);
|
||||
|
||||
/* This sets ifname only when it is not set yet. */
|
||||
|
||||
if (*ifname)
|
||||
return *ifname;
|
||||
return 0;
|
||||
|
||||
if (ifindex <= 0)
|
||||
return NULL;
|
||||
|
||||
if (!format_ifname(ifindex, buf))
|
||||
return NULL;
|
||||
|
||||
return *ifname = strdup(buf);
|
||||
return format_ifname_alloc(ifindex, ifname);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,28 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
const char *get_ifname(int ifindex, char **ifname);
|
||||
#include "log-link.h"
|
||||
|
||||
#define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) { \
|
||||
const char *_n = NULL; \
|
||||
\
|
||||
(void) type##_get_ifname(val, &_n); \
|
||||
log_interface_full_errno_zerook( \
|
||||
_n, LOG_DEBUG, _e, prefix fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
|
||||
#define log_interface_prefix_full_errno(prefix, type, val, error, fmt, ...) \
|
||||
({ \
|
||||
int _error = (error); \
|
||||
ASSERT_NON_ZERO(_error); \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
prefix, type, val, _error, fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
int get_ifname(int ifindex, char **ifname);
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
|
|||
if (len != sizeof_field(sd_dhcp_client_id, eth))
|
||||
return -EINVAL;
|
||||
|
||||
r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
|
||||
r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
client_id->eth.haddr[0],
|
||||
client_id->eth.haddr[1],
|
||||
client_id->eth.haddr[2],
|
||||
|
|
@ -297,11 +297,19 @@ int sd_dhcp_client_set_ifname(sd_dhcp_client *client, const char *ifname) {
|
|||
return free_and_strdup(&client->ifname, ifname);
|
||||
}
|
||||
|
||||
const char *sd_dhcp_client_get_ifname(sd_dhcp_client *client) {
|
||||
if (!client)
|
||||
return NULL;
|
||||
int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret) {
|
||||
int r;
|
||||
|
||||
return get_ifname(client->ifindex, &client->ifname);
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
r = get_ifname(client->ifindex, &client->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = client->ifname;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_mac(
|
||||
|
|
@ -726,7 +734,7 @@ static int client_notify(sd_dhcp_client *client, int event) {
|
|||
static int client_initialize(sd_dhcp_client *client) {
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
client->receive_message = sd_event_source_unref(client->receive_message);
|
||||
client->receive_message = sd_event_source_disable_unref(client->receive_message);
|
||||
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
|
|
@ -1308,7 +1316,7 @@ static int client_timeout_resend(
|
|||
goto error;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unhandled choice");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
|
|
@ -1492,7 +1500,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
|
|||
|
||||
assert(client);
|
||||
|
||||
client->receive_message = sd_event_source_unref(client->receive_message);
|
||||
client->receive_message = sd_event_source_disable_unref(client->receive_message);
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
client->state = DHCP_STATE_REBINDING;
|
||||
|
|
@ -1844,7 +1852,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
|
||||
client->start_delay = 0;
|
||||
(void) event_source_disable(client->timeout_resend);
|
||||
client->receive_message = sd_event_source_unref(client->receive_message);
|
||||
client->receive_message = sd_event_source_disable_unref(client->receive_message);
|
||||
client->fd = safe_close(client->fd);
|
||||
|
||||
client->state = DHCP_STATE_BOUND;
|
||||
|
|
@ -1896,7 +1904,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
r = -EINVAL;
|
||||
goto error;
|
||||
default:
|
||||
assert_not_reached("invalid state");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
error:
|
||||
|
|
@ -2226,17 +2234,15 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
|
|||
|
||||
log_dhcp_client(client, "FREE");
|
||||
|
||||
client_initialize(client);
|
||||
|
||||
client->timeout_resend = sd_event_source_unref(client->timeout_resend);
|
||||
client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
|
||||
client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
|
||||
client->timeout_expire = sd_event_source_unref(client->timeout_expire);
|
||||
|
||||
client_initialize(client);
|
||||
|
||||
sd_dhcp_client_detach_event(client);
|
||||
|
||||
sd_dhcp_lease_unref(client->lease);
|
||||
|
||||
set_free(client->req_opts);
|
||||
free(client->hostname);
|
||||
free(client->vendor_class_identifier);
|
||||
|
|
|
|||
|
|
@ -179,11 +179,19 @@ int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) {
|
|||
return free_and_strdup(&client->ifname, ifname);
|
||||
}
|
||||
|
||||
const char *sd_dhcp6_client_get_ifname(sd_dhcp6_client *client) {
|
||||
if (!client)
|
||||
return NULL;
|
||||
int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) {
|
||||
int r;
|
||||
|
||||
return get_ifname(client->ifindex, &client->ifname);
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
r = get_ifname(client->ifindex, &client->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = client->ifname;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_client_set_local_address(
|
||||
|
|
@ -1101,11 +1109,9 @@ static int client_parse_message(
|
|||
size_t len,
|
||||
sd_dhcp6_lease *lease) {
|
||||
|
||||
uint16_t ia_na_status = 0, ia_pd_status = 0;
|
||||
uint32_t lt_t1 = ~0, lt_t2 = ~0;
|
||||
uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
|
||||
usec_t irt = IRT_DEFAULT;
|
||||
bool clientid = false;
|
||||
size_t pos = 0;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
|
|
@ -1114,50 +1120,35 @@ static int client_parse_message(
|
|||
assert(lease);
|
||||
|
||||
len -= sizeof(DHCP6Message);
|
||||
for (size_t offset = 0; offset < len;) {
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
const uint8_t *optval;
|
||||
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *) &message->options[pos];
|
||||
uint16_t optcode, optlen;
|
||||
be32_t iaid_lease;
|
||||
int status;
|
||||
uint8_t *optval;
|
||||
|
||||
if (len < pos + offsetof(DHCP6Option, data))
|
||||
return -ENOBUFS;
|
||||
|
||||
optcode = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
optval = option->data;
|
||||
|
||||
if (len < pos + offsetof(DHCP6Option, data) + optlen)
|
||||
return -ENOBUFS;
|
||||
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
if (clientid) {
|
||||
log_dhcp6_client(client, "%s contains multiple clientids",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (clientid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
if (optlen != client->duid_len ||
|
||||
memcmp(&client->duid, optval, optlen) != 0) {
|
||||
log_dhcp6_client(client, "%s DUID does not match",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
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));
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
clientid = true;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_SERVERID:
|
||||
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
|
||||
if (r >= 0) {
|
||||
log_dhcp6_client(client, "%s contains multiple serverids",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r >= 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
r = dhcp6_lease_set_serverid(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
|
|
@ -1175,87 +1166,75 @@ static int client_parse_message(
|
|||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
|
||||
if (status < 0)
|
||||
return status;
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
_cleanup_free_ char *msg = NULL;
|
||||
|
||||
if (status > 0) {
|
||||
log_dhcp6_client(client, "%s Status %s",
|
||||
dhcp6_message_type_to_string(message->type),
|
||||
dhcp6_message_status_to_string(status));
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Information request ignoring IA NA option");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, option, &lease->ia, &ia_na_status);
|
||||
if (r < 0 && r != -ENOMSG)
|
||||
return r;
|
||||
|
||||
if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
|
||||
pos += offsetof(DHCP6Option, data) + optlen;
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp6_lease_get_iaid(lease, &iaid_lease);
|
||||
r = dhcp6_option_parse_status(optval, optlen, &msg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (client->ia_na.ia_na.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID for IA NA",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received %s message with non-zero status: %s%s%s",
|
||||
dhcp6_message_type_to_string(message->type),
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_NA: {
|
||||
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (lease->ia.addresses) {
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Information request ignoring IA PD option");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, option, &lease->pd, &ia_pd_status);
|
||||
if (r < 0 && r != -ENOMSG)
|
||||
return r;
|
||||
|
||||
if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
|
||||
pos += offsetof(DHCP6Option, data) + optlen;
|
||||
log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
lease->ia = ia;
|
||||
ia = (DHCP6IA) {};
|
||||
|
||||
if (client->ia_pd.ia_pd.id != iaid_lease) {
|
||||
log_dhcp6_client(client, "%s has wrong IAID for IA PD",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
|
||||
}
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
|
||||
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_PD: {
|
||||
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
lease->pd = ia;
|
||||
ia = (DHCP6IA) {};
|
||||
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
|
||||
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
||||
r = dhcp6_lease_set_rapid_commit(lease);
|
||||
if (r < 0)
|
||||
|
|
@ -1264,28 +1243,28 @@ static int client_parse_message(
|
|||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_DNS_SERVERS:
|
||||
r = dhcp6_lease_set_dns(lease, optval, optlen);
|
||||
r = dhcp6_lease_add_dns(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
||||
r = dhcp6_lease_set_domains(lease, optval, optlen);
|
||||
r = dhcp6_lease_add_domains(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_NTP_SERVER:
|
||||
r = dhcp6_lease_set_ntp(lease, optval, optlen);
|
||||
r = dhcp6_lease_add_ntp(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
||||
r = dhcp6_lease_set_sntp(lease, optval, optlen);
|
||||
r = dhcp6_lease_add_sntp(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -1305,30 +1284,21 @@ static int client_parse_message(
|
|||
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += offsetof(DHCP6Option, data) + optlen;
|
||||
}
|
||||
|
||||
if (ia_na_status > 0 && ia_pd_status > 0) {
|
||||
log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!clientid) {
|
||||
log_dhcp6_client(client, "%s has incomplete options",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!clientid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
|
||||
if (r < 0) {
|
||||
log_dhcp6_client(client, "%s has no server id",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_dhcp6_client_errno(client, r, "%s has no server id",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
if (!lease->ia.addresses && !lease->pd.addresses)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
|
||||
|
||||
} else {
|
||||
if (lease->ia.addresses) {
|
||||
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
|
|
@ -1479,15 +1449,16 @@ static int client_receive_message(
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (client->transaction_id != (message->transaction_id &
|
||||
htobe32(0x00ffffff)))
|
||||
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
|
||||
return 0;
|
||||
|
||||
switch (client->state) {
|
||||
case DHCP6_STATE_INFORMATION_REQUEST:
|
||||
r = client_receive_reply(client, message, len);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
|
||||
|
||||
|
|
@ -1497,10 +1468,13 @@ static int client_receive_message(
|
|||
|
||||
case DHCP6_STATE_SOLICITATION:
|
||||
r = client_receive_advertise(client, message, len);
|
||||
if (r < 0) {
|
||||
log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r == DHCP6_STATE_REQUEST) {
|
||||
client_start(client, r);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1510,11 +1484,12 @@ static int client_receive_message(
|
|||
case DHCP6_STATE_REBIND:
|
||||
|
||||
r = client_receive_reply(client, message, len);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r == DHCP6_STATE_BOUND) {
|
||||
|
||||
r = client_start(client, DHCP6_STATE_BOUND);
|
||||
if (r < 0) {
|
||||
client_stop(client, r);
|
||||
|
|
|
|||
|
|
@ -193,86 +193,69 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
|||
lease->prefix_iter = lease->pd.addresses;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
if (!optlen)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
|
||||
lease->dns_count);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lease->dns_count = r;
|
||||
|
||||
return 0;
|
||||
return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) {
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(addrs, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->dns_count) {
|
||||
*addrs = lease->dns;
|
||||
return lease->dns_count;
|
||||
}
|
||||
if (!lease->dns)
|
||||
return -ENOENT;
|
||||
|
||||
return -ENOENT;
|
||||
*ret = lease->dns;
|
||||
return lease->dns_count;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
|
||||
size_t optlen) {
|
||||
int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
int r;
|
||||
char **domains;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
if (!optlen)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
return r;
|
||||
|
||||
strv_free_and_replace(lease->domains, domains);
|
||||
lease->domains_count = r;
|
||||
|
||||
return r;
|
||||
return strv_extend_strv(&lease->domains, domains, true);
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) {
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(domains, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->domains_count) {
|
||||
*domains = lease->domains;
|
||||
return lease->domains_count;
|
||||
}
|
||||
if (!lease->domains)
|
||||
return -ENOENT;
|
||||
|
||||
return -ENOENT;
|
||||
*ret = lease->domains;
|
||||
return strv_length(lease->domains);
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
uint16_t subopt;
|
||||
size_t sublen;
|
||||
uint8_t *subval;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
lease->ntp = mfree(lease->ntp);
|
||||
lease->ntp_count = 0;
|
||||
for (size_t offset = 0; offset < optlen;) {
|
||||
const uint8_t *subval;
|
||||
size_t sublen;
|
||||
uint16_t subopt;
|
||||
|
||||
while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
|
||||
&subval)) >= 0) {
|
||||
int s;
|
||||
char **servers;
|
||||
r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch(subopt) {
|
||||
case DHCP6_NTP_SUBOPTION_SRV_ADDR:
|
||||
|
|
@ -280,86 +263,74 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
|||
if (sublen != 16)
|
||||
return 0;
|
||||
|
||||
s = dhcp6_option_parse_ip6addrs(subval, sublen,
|
||||
&lease->ntp,
|
||||
lease->ntp_count);
|
||||
if (s < 0)
|
||||
return s;
|
||||
|
||||
lease->ntp_count = s;
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_NTP_SUBOPTION_SRV_FQDN:
|
||||
r = dhcp6_option_parse_domainname_list(subval, sublen,
|
||||
&servers);
|
||||
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
strv_free_and_replace(lease->ntp_fqdn, servers);
|
||||
lease->ntp_fqdn_count = r;
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r != -ENOMSG)
|
||||
return r;
|
||||
case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
|
||||
_cleanup_free_ char *server = NULL;
|
||||
|
||||
r = dhcp6_option_parse_domainname(subval, sublen, &server);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_contains(lease->ntp_fqdn, server))
|
||||
continue;
|
||||
|
||||
r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
}}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
if (!optlen)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
if (lease->ntp || lease->ntp_fqdn)
|
||||
return -EEXIST;
|
||||
|
||||
/* Using deprecated SNTP information */
|
||||
|
||||
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
|
||||
lease->ntp_count);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lease->ntp_count = r;
|
||||
|
||||
return 0;
|
||||
/* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
|
||||
return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
|
||||
const struct in6_addr **addrs) {
|
||||
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(addrs, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->ntp_count) {
|
||||
*addrs = lease->ntp;
|
||||
if (lease->ntp) {
|
||||
*ret = lease->ntp;
|
||||
return lease->ntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(ntp_fqdn, -EINVAL);
|
||||
|
||||
if (lease->ntp_fqdn_count) {
|
||||
*ntp_fqdn = lease->ntp_fqdn;
|
||||
return lease->ntp_fqdn_count;
|
||||
if (lease->sntp && !lease->ntp_fqdn) {
|
||||
/* Fallback to the deprecated SNTP option. */
|
||||
*ret = lease->sntp;
|
||||
return lease->sntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
|
||||
size_t optlen) {
|
||||
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;
|
||||
return strv_length(lease->ntp_fqdn);
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
char *fqdn;
|
||||
|
||||
|
|
@ -378,33 +349,31 @@ int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
|
|||
return free_and_replace(lease->fqdn, fqdn);
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) {
|
||||
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(fqdn, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->fqdn) {
|
||||
*fqdn = lease->fqdn;
|
||||
return 0;
|
||||
}
|
||||
if (!lease->fqdn)
|
||||
return -ENOENT;
|
||||
|
||||
return -ENOENT;
|
||||
*ret = lease->fqdn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
||||
assert(lease);
|
||||
if (!lease)
|
||||
return NULL;
|
||||
|
||||
free(lease->serverid);
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
|
||||
free(lease->dns);
|
||||
free(lease->fqdn);
|
||||
|
||||
lease->domains = strv_free(lease->domains);
|
||||
|
||||
strv_free(lease->domains);
|
||||
free(lease->ntp);
|
||||
strv_free(lease->ntp_fqdn);
|
||||
free(lease->sntp);
|
||||
|
||||
lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
|
||||
return mfree(lease);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "log-link.h"
|
||||
#include "memory-util.h"
|
||||
#include "network-common.h"
|
||||
#include "random-util.h"
|
||||
|
|
@ -81,12 +80,12 @@ struct sd_ipv4acd {
|
|||
#define log_ipv4acd_errno(acd, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"IPv4ACD: ", \
|
||||
sd_ipv4acd_get_ifname(acd), \
|
||||
sd_ipv4acd, acd, \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4acd(acd, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"IPv4ACD: ", \
|
||||
sd_ipv4acd_get_ifname(acd), \
|
||||
sd_ipv4acd, acd, \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
|
||||
|
|
@ -120,7 +119,7 @@ static void ipv4acd_reset(sd_ipv4acd *acd) {
|
|||
assert(acd);
|
||||
|
||||
(void) event_source_disable(acd->timer_event_source);
|
||||
acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source);
|
||||
acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source);
|
||||
|
||||
acd->fd = safe_close(acd->fd);
|
||||
|
||||
|
|
@ -130,9 +129,8 @@ static void ipv4acd_reset(sd_ipv4acd *acd) {
|
|||
static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
acd->timer_event_source = sd_event_source_unref(acd->timer_event_source);
|
||||
|
||||
ipv4acd_reset(acd);
|
||||
sd_event_source_unref(acd->timer_event_source);
|
||||
sd_ipv4acd_detach_event(acd);
|
||||
free(acd->ifname);
|
||||
return mfree(acd);
|
||||
|
|
@ -293,7 +291,7 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid state.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -409,7 +407,7 @@ static int ipv4acd_on_packet(
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid state.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -446,11 +444,19 @@ int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) {
|
|||
return free_and_strdup(&acd->ifname, ifname);
|
||||
}
|
||||
|
||||
const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) {
|
||||
if (!acd)
|
||||
return NULL;
|
||||
int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) {
|
||||
int r;
|
||||
|
||||
return get_ifname(acd->ifindex, &acd->ifname);
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
r = get_ifname(acd->ifindex, &acd->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = acd->ifname;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include "alloc-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "log-link.h"
|
||||
#include "network-common.h"
|
||||
#include "random-util.h"
|
||||
#include "siphash24.h"
|
||||
#include "sparse-endian.h"
|
||||
|
|
@ -55,12 +55,12 @@ struct sd_ipv4ll {
|
|||
#define log_ipv4ll_errno(ll, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"IPv4LL: ", \
|
||||
sd_ipv4ll_get_ifname(ll), \
|
||||
sd_ipv4ll, ll, \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4ll(ll, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"IPv4LL: ", \
|
||||
sd_ipv4ll_get_ifname(ll), \
|
||||
sd_ipv4ll, ll, \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
|
||||
|
|
@ -133,11 +133,10 @@ int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *ifname) {
|
|||
return sd_ipv4acd_set_ifname(ll->acd, ifname);
|
||||
}
|
||||
|
||||
const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll) {
|
||||
if (!ll)
|
||||
return NULL;
|
||||
int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
return sd_ipv4acd_get_ifname(ll->acd);
|
||||
return sd_ipv4acd_get_ifname(ll->acd, ret);
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
|
||||
|
|
@ -360,7 +359,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid IPv4ACD event.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
|||
523
src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c
Normal file
523
src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "sd-lldp-rx.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "lldp-neighbor.h"
|
||||
#include "lldp-network.h"
|
||||
#include "lldp-rx-internal.h"
|
||||
#include "memory-util.h"
|
||||
#include "network-common.h"
|
||||
#include "socket-util.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-table.h"
|
||||
|
||||
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
|
||||
|
||||
static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = {
|
||||
[SD_LLDP_RX_EVENT_ADDED] = "added",
|
||||
[SD_LLDP_RX_EVENT_REMOVED] = "removed",
|
||||
[SD_LLDP_RX_EVENT_UPDATED] = "updated",
|
||||
[SD_LLDP_RX_EVENT_REFRESHED] = "refreshed",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t);
|
||||
|
||||
static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) {
|
||||
assert(lldp_rx);
|
||||
|
||||
hashmap_clear(lldp_rx->neighbor_by_id);
|
||||
}
|
||||
|
||||
static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) {
|
||||
assert(lldp_rx);
|
||||
assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX);
|
||||
|
||||
if (!lldp_rx->callback)
|
||||
return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event));
|
||||
|
||||
log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event));
|
||||
lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata);
|
||||
}
|
||||
|
||||
static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) {
|
||||
usec_t t = USEC_INFINITY;
|
||||
bool changed = false;
|
||||
|
||||
assert(lldp_rx);
|
||||
|
||||
/* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
|
||||
* are free. */
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
||||
|
||||
n = prioq_peek(lldp_rx->neighbor_by_expiry);
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
sd_lldp_neighbor_ref(n);
|
||||
|
||||
if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra))
|
||||
goto remove_one;
|
||||
|
||||
if (t == USEC_INFINITY)
|
||||
t = now(clock_boottime_or_monotonic());
|
||||
|
||||
if (n->until > t)
|
||||
break;
|
||||
|
||||
remove_one:
|
||||
lldp_neighbor_unlink(n);
|
||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
|
||||
assert(lldp_rx);
|
||||
assert(n);
|
||||
|
||||
/* Don't keep data with a zero TTL */
|
||||
if (n->ttl <= 0)
|
||||
return false;
|
||||
|
||||
/* Filter out data from the filter address */
|
||||
if (!ether_addr_is_null(&lldp_rx->filter_address) &&
|
||||
ether_addr_equal(&lldp_rx->filter_address, &n->source_address))
|
||||
return false;
|
||||
|
||||
/* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
|
||||
* no caps field set. */
|
||||
if (n->has_capabilities &&
|
||||
(n->enabled_capabilities & lldp_rx->capability_mask) == 0)
|
||||
return false;
|
||||
|
||||
/* Keep everything else */
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor);
|
||||
|
||||
static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
|
||||
bool keep;
|
||||
int r;
|
||||
|
||||
assert(lldp_rx);
|
||||
assert(n);
|
||||
assert(!n->lldp_rx);
|
||||
|
||||
keep = lldp_rx_keep_neighbor(lldp_rx, n);
|
||||
|
||||
/* First retrieve the old entry for this MSAP */
|
||||
old = hashmap_get(lldp_rx->neighbor_by_id, &n->id);
|
||||
if (old) {
|
||||
sd_lldp_neighbor_ref(old);
|
||||
|
||||
if (!keep) {
|
||||
lldp_neighbor_unlink(old);
|
||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lldp_neighbor_equal(n, old)) {
|
||||
/* Is this equal, then restart the TTL counter, but don't do anything else. */
|
||||
old->timestamp = n->timestamp;
|
||||
lldp_rx_start_timer(lldp_rx, old);
|
||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Data changed, remove the old entry, and add a new one */
|
||||
lldp_neighbor_unlink(old);
|
||||
|
||||
} else if (!keep)
|
||||
return 0;
|
||||
|
||||
/* Then, make room for at least one new neighbor */
|
||||
lldp_rx_make_space(lldp_rx, 1);
|
||||
|
||||
r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx);
|
||||
if (r < 0) {
|
||||
assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
n->lldp_rx = lldp_rx;
|
||||
|
||||
lldp_rx_start_timer(lldp_rx, n);
|
||||
lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n);
|
||||
|
||||
return 1;
|
||||
|
||||
finish:
|
||||
if (old)
|
||||
lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
|
||||
int r;
|
||||
|
||||
assert(lldp_rx);
|
||||
assert(n);
|
||||
|
||||
r = lldp_neighbor_parse(n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = lldp_rx_add_neighbor(lldp_rx, n);
|
||||
if (r < 0)
|
||||
return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring.");
|
||||
|
||||
log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
||||
ssize_t space, length;
|
||||
sd_lldp_rx *lldp_rx = userdata;
|
||||
struct timespec ts;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(lldp_rx);
|
||||
|
||||
space = next_datagram_size_fd(fd);
|
||||
if (space < 0) {
|
||||
log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = lldp_neighbor_new(space);
|
||||
if (!n) {
|
||||
log_oom_debug();
|
||||
return 0;
|
||||
}
|
||||
|
||||
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
|
||||
if (length < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
return 0;
|
||||
|
||||
log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((size_t) length != n->raw_size) {
|
||||
log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to get the timestamp of this packet if it is known */
|
||||
if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
|
||||
triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
|
||||
else
|
||||
triple_timestamp_get(&n->timestamp);
|
||||
|
||||
(void) lldp_rx_handle_datagram(lldp_rx, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lldp_rx_reset(sd_lldp_rx *lldp_rx) {
|
||||
assert(lldp_rx);
|
||||
|
||||
(void) event_source_disable(lldp_rx->timer_event_source);
|
||||
lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
|
||||
lldp_rx->fd = safe_close(lldp_rx->fd);
|
||||
}
|
||||
|
||||
int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
|
||||
if (!lldp_rx)
|
||||
return false;
|
||||
|
||||
return lldp_rx->fd >= 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
|
||||
int r;
|
||||
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
assert_return(lldp_rx->event, -EINVAL);
|
||||
assert_return(lldp_rx->ifindex > 0, -EINVAL);
|
||||
|
||||
if (sd_lldp_rx_is_running(lldp_rx))
|
||||
return 0;
|
||||
|
||||
assert(!lldp_rx->io_event_source);
|
||||
|
||||
lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex);
|
||||
if (lldp_rx->fd < 0)
|
||||
return lldp_rx->fd;
|
||||
|
||||
r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io");
|
||||
|
||||
log_lldp_rx(lldp_rx, "Started LLDP client");
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
lldp_rx_reset(lldp_rx);
|
||||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
|
||||
if (!sd_lldp_rx_is_running(lldp_rx))
|
||||
return 0;
|
||||
|
||||
log_lldp_rx(lldp_rx, "Stopping LLDP client");
|
||||
|
||||
lldp_rx_reset(lldp_rx);
|
||||
lldp_rx_flush_neighbors(lldp_rx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
|
||||
assert_return(!lldp_rx->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
lldp_rx->event = sd_event_ref(event);
|
||||
else {
|
||||
r = sd_event_default(&lldp_rx->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
lldp_rx->event_priority = priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ 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);
|
||||
|
||||
lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
|
||||
lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source);
|
||||
lldp_rx->event = sd_event_unref(lldp_rx->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ 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) {
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
|
||||
lldp_rx->callback = cb;
|
||||
lldp_rx->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ 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);
|
||||
|
||||
lldp_rx->ifindex = ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) {
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
assert_return(ifname, -EINVAL);
|
||||
|
||||
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
|
||||
return -EINVAL;
|
||||
|
||||
return free_and_strdup(&lldp_rx->ifname, ifname);
|
||||
}
|
||||
|
||||
int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
|
||||
r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = lldp_rx->ifname;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
|
||||
if (!lldp_rx)
|
||||
return NULL;
|
||||
|
||||
lldp_rx_reset(lldp_rx);
|
||||
|
||||
sd_lldp_rx_detach_event(lldp_rx);
|
||||
|
||||
lldp_rx_flush_neighbors(lldp_rx);
|
||||
|
||||
hashmap_free(lldp_rx->neighbor_by_id);
|
||||
prioq_free(lldp_rx->neighbor_by_expiry);
|
||||
free(lldp_rx->ifname);
|
||||
return mfree(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) {
|
||||
_cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
lldp_rx = new(sd_lldp_rx, 1);
|
||||
if (!lldp_rx)
|
||||
return -ENOMEM;
|
||||
|
||||
*lldp_rx = (sd_lldp_rx) {
|
||||
.n_ref = 1,
|
||||
.fd = -1,
|
||||
.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
|
||||
.capability_mask = UINT16_MAX,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(lldp_rx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_lldp_rx *lldp_rx = userdata;
|
||||
int r;
|
||||
|
||||
r = lldp_rx_make_space(lldp_rx, 0);
|
||||
if (r < 0) {
|
||||
log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = lldp_rx_start_timer(lldp_rx, NULL);
|
||||
if (r < 0) {
|
||||
log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) {
|
||||
sd_lldp_neighbor *n;
|
||||
|
||||
assert(lldp_rx);
|
||||
assert(lldp_rx->event);
|
||||
|
||||
if (neighbor)
|
||||
lldp_neighbor_start_ttl(neighbor);
|
||||
|
||||
n = prioq_peek(lldp_rx->neighbor_by_expiry);
|
||||
if (!n)
|
||||
return event_source_disable(lldp_rx->timer_event_source);
|
||||
|
||||
return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
n->until, 0,
|
||||
on_timer_event, lldp_rx,
|
||||
lldp_rx->event_priority, "lldp-rx-timer", true);
|
||||
}
|
||||
|
||||
static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
assert(*a);
|
||||
assert(*b);
|
||||
|
||||
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) {
|
||||
_cleanup_free_ sd_lldp_neighbor **l = NULL;
|
||||
sd_lldp_neighbor *n;
|
||||
int k = 0;
|
||||
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id));
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id)
|
||||
l[k++] = sd_lldp_neighbor_ref(n);
|
||||
|
||||
assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id));
|
||||
|
||||
/* Return things in a stable order */
|
||||
typesafe_qsort(l, k, neighbor_compare_func);
|
||||
*ret = TAKE_PTR(l);
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
_public_ 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);
|
||||
|
||||
lldp_rx->neighbors_max = m;
|
||||
lldp_rx_make_space(lldp_rx, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
|
||||
assert_return(lldp_rx, -EINVAL);
|
||||
assert_return(mask != 0, -EINVAL);
|
||||
|
||||
lldp_rx->capability_mask = mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ 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
|
||||
* that our own can be filtered out here. */
|
||||
|
||||
if (addr)
|
||||
lldp_rx->filter_address = *addr;
|
||||
else
|
||||
zero(lldp_rx->filter_address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,522 +0,0 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "sd-lldp.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "lldp-internal.h"
|
||||
#include "lldp-neighbor.h"
|
||||
#include "lldp-network.h"
|
||||
#include "memory-util.h"
|
||||
#include "network-common.h"
|
||||
#include "socket-util.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-table.h"
|
||||
|
||||
#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
|
||||
|
||||
static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
|
||||
[SD_LLDP_EVENT_ADDED] = "added",
|
||||
[SD_LLDP_EVENT_REMOVED] = "removed",
|
||||
[SD_LLDP_EVENT_UPDATED] = "updated",
|
||||
[SD_LLDP_EVENT_REFRESHED] = "refreshed",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event_t);
|
||||
|
||||
static void lldp_flush_neighbors(sd_lldp *lldp) {
|
||||
assert(lldp);
|
||||
|
||||
hashmap_clear(lldp->neighbor_by_id);
|
||||
}
|
||||
|
||||
static void lldp_callback(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n) {
|
||||
assert(lldp);
|
||||
assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
|
||||
|
||||
if (!lldp->callback)
|
||||
return (void) log_lldp(lldp, "Received '%s' event.", lldp_event_to_string(event));
|
||||
|
||||
log_lldp(lldp, "Invoking callback for '%s' event.", lldp_event_to_string(event));
|
||||
lldp->callback(lldp, event, n, lldp->userdata);
|
||||
}
|
||||
|
||||
static int lldp_make_space(sd_lldp *lldp, size_t extra) {
|
||||
usec_t t = USEC_INFINITY;
|
||||
bool changed = false;
|
||||
|
||||
assert(lldp);
|
||||
|
||||
/* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
|
||||
* are free. */
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
||||
|
||||
n = prioq_peek(lldp->neighbor_by_expiry);
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
sd_lldp_neighbor_ref(n);
|
||||
|
||||
if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
|
||||
goto remove_one;
|
||||
|
||||
if (t == USEC_INFINITY)
|
||||
t = now(clock_boottime_or_monotonic());
|
||||
|
||||
if (n->until > t)
|
||||
break;
|
||||
|
||||
remove_one:
|
||||
lldp_neighbor_unlink(n);
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
|
||||
assert(lldp);
|
||||
assert(n);
|
||||
|
||||
/* Don't keep data with a zero TTL */
|
||||
if (n->ttl <= 0)
|
||||
return false;
|
||||
|
||||
/* Filter out data from the filter address */
|
||||
if (!ether_addr_is_null(&lldp->filter_address) &&
|
||||
ether_addr_equal(&lldp->filter_address, &n->source_address))
|
||||
return false;
|
||||
|
||||
/* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
|
||||
* no caps field set. */
|
||||
if (n->has_capabilities &&
|
||||
(n->enabled_capabilities & lldp->capability_mask) == 0)
|
||||
return false;
|
||||
|
||||
/* Keep everything else */
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
|
||||
|
||||
static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
|
||||
bool keep;
|
||||
int r;
|
||||
|
||||
assert(lldp);
|
||||
assert(n);
|
||||
assert(!n->lldp);
|
||||
|
||||
keep = lldp_keep_neighbor(lldp, n);
|
||||
|
||||
/* First retrieve the old entry for this MSAP */
|
||||
old = hashmap_get(lldp->neighbor_by_id, &n->id);
|
||||
if (old) {
|
||||
sd_lldp_neighbor_ref(old);
|
||||
|
||||
if (!keep) {
|
||||
lldp_neighbor_unlink(old);
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lldp_neighbor_equal(n, old)) {
|
||||
/* Is this equal, then restart the TTL counter, but don't do anything else. */
|
||||
old->timestamp = n->timestamp;
|
||||
lldp_start_timer(lldp, old);
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Data changed, remove the old entry, and add a new one */
|
||||
lldp_neighbor_unlink(old);
|
||||
|
||||
} else if (!keep)
|
||||
return 0;
|
||||
|
||||
/* Then, make room for at least one new neighbor */
|
||||
lldp_make_space(lldp, 1);
|
||||
|
||||
r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
|
||||
if (r < 0) {
|
||||
assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
n->lldp = lldp;
|
||||
|
||||
lldp_start_timer(lldp, n);
|
||||
lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
|
||||
|
||||
return 1;
|
||||
|
||||
finish:
|
||||
if (old)
|
||||
lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
|
||||
int r;
|
||||
|
||||
assert(lldp);
|
||||
assert(n);
|
||||
|
||||
r = lldp_neighbor_parse(n);
|
||||
if (r == -EBADMSG) /* Ignore bad messages */
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = lldp_add_neighbor(lldp, n);
|
||||
if (r < 0) {
|
||||
log_lldp_errno(lldp, r, "Failed to add datagram. Ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_lldp(lldp, "Successfully processed LLDP datagram.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
|
||||
ssize_t space, length;
|
||||
sd_lldp *lldp = userdata;
|
||||
struct timespec ts;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(lldp);
|
||||
|
||||
space = next_datagram_size_fd(fd);
|
||||
if (space < 0) {
|
||||
log_lldp_errno(lldp, space, "Failed to determine datagram size to read, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = lldp_neighbor_new(space);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
|
||||
if (length < 0) {
|
||||
if (IN_SET(errno, EAGAIN, EINTR))
|
||||
return 0;
|
||||
|
||||
log_lldp_errno(lldp, errno, "Failed to read LLDP datagram, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((size_t) length != n->raw_size) {
|
||||
log_lldp(lldp, "Packet size mismatch, ignoring");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to get the timestamp of this packet if it is known */
|
||||
if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
|
||||
triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
|
||||
else
|
||||
triple_timestamp_get(&n->timestamp);
|
||||
|
||||
return lldp_handle_datagram(lldp, n);
|
||||
}
|
||||
|
||||
static void lldp_reset(sd_lldp *lldp) {
|
||||
assert(lldp);
|
||||
|
||||
(void) event_source_disable(lldp->timer_event_source);
|
||||
lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
|
||||
lldp->fd = safe_close(lldp->fd);
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_start(sd_lldp *lldp) {
|
||||
int r;
|
||||
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(lldp->event, -EINVAL);
|
||||
assert_return(lldp->ifindex > 0, -EINVAL);
|
||||
|
||||
if (lldp->fd >= 0)
|
||||
return 0;
|
||||
|
||||
assert(!lldp->io_event_source);
|
||||
|
||||
lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
|
||||
if (lldp->fd < 0)
|
||||
return lldp->fd;
|
||||
|
||||
r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
|
||||
|
||||
log_lldp(lldp, "Started LLDP client");
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
lldp_reset(lldp);
|
||||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_stop(sd_lldp *lldp) {
|
||||
if (!lldp)
|
||||
return 0;
|
||||
|
||||
if (lldp->fd < 0)
|
||||
return 0;
|
||||
|
||||
log_lldp(lldp, "Stopping LLDP client");
|
||||
|
||||
lldp_reset(lldp);
|
||||
lldp_flush_neighbors(lldp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(lldp->fd < 0, -EBUSY);
|
||||
assert_return(!lldp->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
lldp->event = sd_event_ref(event);
|
||||
else {
|
||||
r = sd_event_default(&lldp->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
lldp->event_priority = priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_detach_event(sd_lldp *lldp) {
|
||||
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(lldp->fd < 0, -EBUSY);
|
||||
|
||||
lldp->event = sd_event_unref(lldp->event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
|
||||
assert_return(lldp, NULL);
|
||||
|
||||
return lldp->event;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
|
||||
lldp->callback = cb;
|
||||
lldp->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
assert_return(lldp->fd < 0, -EBUSY);
|
||||
|
||||
lldp->ifindex = ifindex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(ifname, -EINVAL);
|
||||
|
||||
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
|
||||
return -EINVAL;
|
||||
|
||||
return free_and_strdup(&lldp->ifname, ifname);
|
||||
}
|
||||
|
||||
const char *sd_lldp_get_ifname(sd_lldp *lldp) {
|
||||
if (!lldp)
|
||||
return NULL;
|
||||
|
||||
return get_ifname(lldp->ifindex, &lldp->ifname);
|
||||
}
|
||||
|
||||
static sd_lldp* lldp_free(sd_lldp *lldp) {
|
||||
assert(lldp);
|
||||
|
||||
lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
|
||||
|
||||
lldp_reset(lldp);
|
||||
sd_lldp_detach_event(lldp);
|
||||
lldp_flush_neighbors(lldp);
|
||||
|
||||
hashmap_free(lldp->neighbor_by_id);
|
||||
prioq_free(lldp->neighbor_by_expiry);
|
||||
free(lldp->ifname);
|
||||
return mfree(lldp);
|
||||
}
|
||||
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
|
||||
|
||||
_public_ int sd_lldp_new(sd_lldp **ret) {
|
||||
_cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
lldp = new(sd_lldp, 1);
|
||||
if (!lldp)
|
||||
return -ENOMEM;
|
||||
|
||||
*lldp = (sd_lldp) {
|
||||
.n_ref = 1,
|
||||
.fd = -1,
|
||||
.neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
|
||||
.capability_mask = UINT16_MAX,
|
||||
};
|
||||
|
||||
lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
|
||||
if (!lldp->neighbor_by_id)
|
||||
return -ENOMEM;
|
||||
|
||||
r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(lldp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
|
||||
return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
|
||||
}
|
||||
|
||||
static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_lldp *lldp = userdata;
|
||||
int r;
|
||||
|
||||
r = lldp_make_space(lldp, 0);
|
||||
if (r < 0) {
|
||||
log_lldp_errno(lldp, r, "Failed to make space, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = lldp_start_timer(lldp, NULL);
|
||||
if (r < 0) {
|
||||
log_lldp_errno(lldp, r, "Failed to restart timer, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
|
||||
sd_lldp_neighbor *n;
|
||||
|
||||
assert(lldp);
|
||||
|
||||
if (neighbor)
|
||||
lldp_neighbor_start_ttl(neighbor);
|
||||
|
||||
n = prioq_peek(lldp->neighbor_by_expiry);
|
||||
if (!n)
|
||||
return event_source_disable(lldp->timer_event_source);
|
||||
|
||||
if (!lldp->event)
|
||||
return 0;
|
||||
|
||||
return event_reset_time(lldp->event, &lldp->timer_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
n->until, 0,
|
||||
on_timer_event, lldp,
|
||||
lldp->event_priority, "lldp-timer", true);
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
|
||||
sd_lldp_neighbor **l = NULL, *n;
|
||||
int k = 0, r;
|
||||
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
r = lldp_start_timer(lldp, NULL);
|
||||
if (r < 0) {
|
||||
free(l);
|
||||
return r;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(n, lldp->neighbor_by_id)
|
||||
l[k++] = sd_lldp_neighbor_ref(n);
|
||||
|
||||
assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
|
||||
|
||||
/* Return things in a stable order */
|
||||
typesafe_qsort(l, k, neighbor_compare_func);
|
||||
*ret = l;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(m > 0, -EINVAL);
|
||||
|
||||
lldp->neighbors_max = m;
|
||||
lldp_make_space(lldp, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
assert_return(mask != 0, -EINVAL);
|
||||
|
||||
lldp->capability_mask = mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
|
||||
assert_return(lldp, -EINVAL);
|
||||
|
||||
/* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
|
||||
* that our own can be filtered out here. */
|
||||
|
||||
if (addr)
|
||||
lldp->filter_address = *addr;
|
||||
else
|
||||
zero(lldp->filter_address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "inotify-util.h"
|
||||
#include "list.h"
|
||||
#include "prioq.h"
|
||||
#include "ratelimit.h"
|
||||
|
|
|
|||
|
|
@ -915,7 +915,7 @@ static void source_disconnect(sd_event_source *s) {
|
|||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Wut? I shouldn't exist.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (s->pending)
|
||||
|
|
@ -2407,7 +2407,7 @@ static int event_source_offline(
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Wut? I shouldn't exist.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
/* Always reshuffle time prioq, as the ratelimited flag may be changed. */
|
||||
|
|
@ -2495,7 +2495,7 @@ static int event_source_online(
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Wut? I shouldn't exist.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
s->enabled = enabled;
|
||||
|
|
@ -3569,7 +3569,7 @@ static int source_dispatch(sd_event_source *s) {
|
|||
case SOURCE_WATCHDOG:
|
||||
case _SOURCE_EVENT_SOURCE_TYPE_MAX:
|
||||
case _SOURCE_EVENT_SOURCE_TYPE_INVALID:
|
||||
assert_not_reached("Wut? I shouldn't exist.");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
s->dispatching = false;
|
||||
|
|
@ -3929,7 +3929,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unexpected event source type");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -3953,7 +3953,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
|
|||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Invalid wake-up pointer");
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
|
|
@ -4154,7 +4154,7 @@ _public_ int sd_event_loop(sd_event *e) {
|
|||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
|
||||
|
||||
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
|
||||
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
|
||||
|
||||
while (e->state != SD_EVENT_FINISHED) {
|
||||
r = sd_event_run(e, UINT64_MAX);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "sync-util.h"
|
||||
|
||||
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
|
||||
unsigned n, k = 0;
|
||||
|
|
@ -167,10 +167,7 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
|
|||
return r;
|
||||
|
||||
if (do_sync) {
|
||||
if (fsync(fd) < 0)
|
||||
return -errno;
|
||||
|
||||
r = fsync_directory_of_file(fd);
|
||||
r = fsync_full(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]);
|
||||
|
||||
#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {})
|
||||
|
||||
bool id128_is_valid(const char *s) _pure_;
|
||||
|
||||
typedef enum Id128Format {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ int sd_dhcp_client_set_ifindex(
|
|||
int sd_dhcp_client_set_ifname(
|
||||
sd_dhcp_client *client,
|
||||
const char *interface_name);
|
||||
const char *sd_dhcp_client_get_ifname(sd_dhcp_client *client);
|
||||
int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret);
|
||||
int sd_dhcp_client_set_mac(
|
||||
sd_dhcp_client *client,
|
||||
const uint8_t *addr,
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ int sd_dhcp6_client_set_ifindex(
|
|||
int sd_dhcp6_client_set_ifname(
|
||||
sd_dhcp6_client *client,
|
||||
const char *interface_name);
|
||||
const char * sd_dhcp6_client_get_ifname(sd_dhcp6_client *client);
|
||||
int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret);
|
||||
int sd_dhcp6_client_set_local_address(
|
||||
sd_dhcp6_client *client,
|
||||
const struct in6_addr *local_address);
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
|
|||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid);
|
||||
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
|
||||
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
|
||||
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn);
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
|
||||
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
|
||||
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
|
||||
|
||||
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
|
||||
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ union sd_id128 {
|
|||
uint64_t qwords[2];
|
||||
};
|
||||
|
||||
#define SD_ID128_STRING_MAX 33
|
||||
#define SD_ID128_STRING_MAX 33U
|
||||
|
||||
char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
|
||||
int sd_id128_from_string(const char *s, sd_id128_t *ret);
|
||||
|
||||
#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
|
||||
|
||||
int sd_id128_randomize(sd_id128_t *ret);
|
||||
|
||||
int sd_id128_get_machine(sd_id128_t *ret);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
|
|||
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
|
||||
int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *interface_name);
|
||||
const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret);
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address);
|
||||
int sd_ipv4acd_is_running(sd_ipv4acd *acd);
|
||||
int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
|
|||
int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
|
||||
int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *interface_name);
|
||||
const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret);
|
||||
int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
|
||||
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
|
||||
int sd_ipv4ll_is_running(sd_ipv4ll *ll);
|
||||
|
|
|
|||
109
src/libnm-systemd-core/src/systemd/sd-lldp-rx.h
Normal file
109
src/libnm-systemd-core/src/systemd/sd-lldp-rx.h
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#ifndef foosdlldprxhfoo
|
||||
#define foosdlldprxhfoo
|
||||
|
||||
/***
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
#include "sd-lldp.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
_SD_BEGIN_DECLARATIONS;
|
||||
|
||||
typedef struct sd_lldp_rx sd_lldp_rx;
|
||||
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
|
||||
|
||||
typedef enum sd_lldp_rx_event_t {
|
||||
SD_LLDP_RX_EVENT_ADDED,
|
||||
SD_LLDP_RX_EVENT_REMOVED,
|
||||
SD_LLDP_RX_EVENT_UPDATED,
|
||||
SD_LLDP_RX_EVENT_REFRESHED,
|
||||
_SD_LLDP_RX_EVENT_MAX,
|
||||
_SD_LLDP_RX_EVENT_INVALID = -EINVAL,
|
||||
_SD_ENUM_FORCE_S64(LLDP_RX_EVENT),
|
||||
} sd_lldp_rx_event_t;
|
||||
|
||||
typedef void (*sd_lldp_rx_callback_t)(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata);
|
||||
|
||||
int sd_lldp_rx_new(sd_lldp_rx **ret);
|
||||
sd_lldp_rx *sd_lldp_rx_ref(sd_lldp_rx *lldp_rx);
|
||||
sd_lldp_rx *sd_lldp_rx_unref(sd_lldp_rx *lldp_rx);
|
||||
|
||||
int sd_lldp_rx_start(sd_lldp_rx *lldp_rx);
|
||||
int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx);
|
||||
int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx);
|
||||
|
||||
int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority);
|
||||
int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx);
|
||||
sd_event *sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx);
|
||||
|
||||
int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata);
|
||||
int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex);
|
||||
int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname);
|
||||
int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret);
|
||||
|
||||
/* Controls how much and what to store in the neighbors database */
|
||||
int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t n);
|
||||
int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask);
|
||||
int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *address);
|
||||
|
||||
int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***neighbors);
|
||||
|
||||
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
|
||||
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
|
||||
sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
|
||||
|
||||
/* Access to LLDP frame metadata */
|
||||
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
||||
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
||||
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
|
||||
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
||||
|
||||
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
|
||||
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_as_string(sd_lldp_neighbor *n, const char **ret);
|
||||
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_as_string(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
|
||||
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
|
||||
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
|
||||
|
||||
/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
|
||||
* (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
|
||||
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
|
||||
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
|
||||
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
|
||||
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
|
||||
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_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype);
|
||||
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_rx, sd_lldp_rx_unref);
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
|
@ -17,12 +17,7 @@
|
|||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "_sd-common.h"
|
||||
|
||||
|
|
@ -85,20 +80,24 @@ enum {
|
|||
|
||||
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \
|
||||
((uint16_t) \
|
||||
(SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \
|
||||
(SD_LLDP_SYSTEM_CAPABILITIES_REPEATER | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_TPMR))
|
||||
|
||||
#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
|
||||
#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
|
||||
#define SD_LLDP_OUI_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 }
|
||||
#define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f }
|
||||
|
||||
#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E }
|
||||
#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01
|
||||
#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E
|
||||
#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA }
|
||||
|
||||
#define SD_LLDP_OUI_IANA_SUBTYPE_MUD 0x01
|
||||
#define SD_LLDP_OUI_IANA_MUD \
|
||||
(const uint8_t[]) { _SD_LLDP_OUI_IANA, SD_LLDP_OUI_IANA_SUBTYPE_MUD }
|
||||
|
||||
/* IEEE 802.1AB-2009 Annex E */
|
||||
enum {
|
||||
|
|
@ -119,80 +118,6 @@ enum {
|
|||
SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4,
|
||||
};
|
||||
|
||||
typedef struct sd_lldp sd_lldp;
|
||||
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
|
||||
|
||||
typedef enum sd_lldp_event_t {
|
||||
SD_LLDP_EVENT_ADDED,
|
||||
SD_LLDP_EVENT_REMOVED,
|
||||
SD_LLDP_EVENT_UPDATED,
|
||||
SD_LLDP_EVENT_REFRESHED,
|
||||
_SD_LLDP_EVENT_MAX,
|
||||
_SD_LLDP_EVENT_INVALID = -EINVAL,
|
||||
_SD_ENUM_FORCE_S64(LLDP_EVENT),
|
||||
} sd_lldp_event_t;
|
||||
|
||||
typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata);
|
||||
|
||||
int sd_lldp_new(sd_lldp **ret);
|
||||
sd_lldp* sd_lldp_ref(sd_lldp *lldp);
|
||||
sd_lldp* sd_lldp_unref(sd_lldp *lldp);
|
||||
|
||||
int sd_lldp_start(sd_lldp *lldp);
|
||||
int sd_lldp_stop(sd_lldp *lldp);
|
||||
|
||||
int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority);
|
||||
int sd_lldp_detach_event(sd_lldp *lldp);
|
||||
sd_event *sd_lldp_get_event(sd_lldp *lldp);
|
||||
|
||||
int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata);
|
||||
int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex);
|
||||
int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname);
|
||||
const char *sd_lldp_get_ifname(sd_lldp *lldp);
|
||||
|
||||
/* Controls how much and what to store in the neighbors database */
|
||||
int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n);
|
||||
int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask);
|
||||
int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address);
|
||||
|
||||
int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors);
|
||||
|
||||
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size);
|
||||
sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n);
|
||||
sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n);
|
||||
|
||||
/* Access to LLDP frame metadata */
|
||||
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
||||
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address);
|
||||
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret);
|
||||
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
||||
|
||||
/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */
|
||||
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_as_string(sd_lldp_neighbor *n, const char **ret);
|
||||
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_as_string(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
|
||||
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
|
||||
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
|
||||
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
|
||||
|
||||
/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
|
||||
* (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
|
||||
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
|
||||
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
|
||||
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type);
|
||||
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type);
|
||||
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_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype);
|
||||
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref);
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ sd_event *sd_ndisc_get_event(sd_ndisc *nd);
|
|||
int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
|
||||
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
|
||||
int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
|
||||
const char *sd_ndisc_get_ifname(sd_ndisc *nd);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -44,16 +44,19 @@ typedef void (*free_func_t)(void *p);
|
|||
|
||||
#define malloc0(n) (calloc(1, (n) ?: 1))
|
||||
|
||||
static inline void *mfree(void *memory) {
|
||||
free(memory);
|
||||
return NULL;
|
||||
}
|
||||
#define mfree(memory) \
|
||||
({ \
|
||||
free(memory); \
|
||||
(typeof(memory)) NULL; \
|
||||
})
|
||||
|
||||
#define free_and_replace(a, b) \
|
||||
({ \
|
||||
free(a); \
|
||||
(a) = (b); \
|
||||
(b) = NULL; \
|
||||
typeof(a)* _a = &(a); \
|
||||
typeof(b)* _b = &(b); \
|
||||
free(*_a); \
|
||||
(*_a) = (*_b); \
|
||||
(*_b) = NULL; \
|
||||
0; \
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ typedef enum CGroupController {
|
|||
CGROUP_CONTROLLER_BPF_DEVICES,
|
||||
CGROUP_CONTROLLER_BPF_FOREIGN,
|
||||
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
|
||||
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
|
||||
|
||||
_CGROUP_CONTROLLER_MAX,
|
||||
_CGROUP_CONTROLLER_INVALID = -EINVAL,
|
||||
|
|
@ -53,6 +54,7 @@ typedef enum CGroupMask {
|
|||
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
|
||||
CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
|
||||
CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
|
||||
CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
|
||||
|
||||
/* All real cgroup v1 controllers */
|
||||
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
|
||||
|
|
@ -61,7 +63,7 @@ typedef enum CGroupMask {
|
|||
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
|
||||
|
||||
/* All cgroup v2 BPF pseudo-controllers */
|
||||
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
|
||||
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
|
||||
|
||||
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
|
||||
} CGroupMask;
|
||||
|
|
@ -172,6 +174,7 @@ typedef enum CGroupFlags {
|
|||
typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
|
||||
|
||||
int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
|
||||
int cg_kill_kernel_sigkill(const char *controller, const char *path);
|
||||
int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
|
||||
|
||||
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
|
||||
|
|
@ -272,6 +275,7 @@ int cg_kernel_controllers(Set **controllers);
|
|||
|
||||
bool cg_ns_supported(void);
|
||||
bool cg_freezer_supported(void);
|
||||
bool cg_kill_supported(void);
|
||||
|
||||
int cg_all_unified(void);
|
||||
int cg_hybrid_unified(void);
|
||||
|
|
|
|||
|
|
@ -183,39 +183,51 @@ static int env_append(char **r, char ***k, char **a) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
char **strv_env_merge(size_t n_lists, ...) {
|
||||
_cleanup_strv_free_ char **ret = NULL;
|
||||
size_t n = 0;
|
||||
char **l, **k;
|
||||
char** _strv_env_merge(char **first, ...) {
|
||||
_cleanup_strv_free_ char **merged = NULL;
|
||||
char **k;
|
||||
va_list ap;
|
||||
|
||||
/* Merges an arbitrary number of environment sets */
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (size_t i = 0; i < n_lists; i++) {
|
||||
size_t n = strv_length(first);
|
||||
|
||||
va_start(ap, first);
|
||||
for (;;) {
|
||||
char **l;
|
||||
|
||||
l = va_arg(ap, char**);
|
||||
if (l == POINTER_MAX)
|
||||
break;
|
||||
|
||||
n += strv_length(l);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
ret = new(char*, n+1);
|
||||
if (!ret)
|
||||
k = merged = new(char*, n + 1);
|
||||
if (!merged)
|
||||
return NULL;
|
||||
merged[0] = NULL;
|
||||
|
||||
if (env_append(merged, &k, first) < 0)
|
||||
return NULL;
|
||||
|
||||
*ret = NULL;
|
||||
k = ret;
|
||||
va_start(ap, first);
|
||||
for (;;) {
|
||||
char **l;
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (size_t i = 0; i < n_lists; i++) {
|
||||
l = va_arg(ap, char**);
|
||||
if (env_append(ret, &k, l) < 0) {
|
||||
if (l == POINTER_MAX)
|
||||
break;
|
||||
|
||||
if (env_append(merged, &k, l) < 0) {
|
||||
va_end(ap);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return TAKE_PTR(ret);
|
||||
return TAKE_PTR(merged);
|
||||
}
|
||||
|
||||
static bool env_match(const char *t, const char *pattern) {
|
||||
|
|
@ -409,6 +421,32 @@ int strv_env_replace_strdup(char ***l, const char *assignment) {
|
|||
return strv_env_replace_consume(l, p);
|
||||
}
|
||||
|
||||
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
|
||||
/* Like strv_env_replace_strdup(), but pulls the variable from the environment of
|
||||
* the calling program, if a variable name without value is specified.
|
||||
*/
|
||||
char *p;
|
||||
|
||||
if (strchr(assignment, '=')) {
|
||||
if (!env_assignment_is_valid(assignment))
|
||||
return -EINVAL;
|
||||
|
||||
p = strdup(assignment);
|
||||
} else {
|
||||
if (!env_name_is_valid(assignment))
|
||||
return -EINVAL;
|
||||
|
||||
/* If we can't find the variable in our environment, we will use
|
||||
* the empty string. This way "passthrough" is equivalent to passing
|
||||
* --setenv=FOO=$FOO in the shell. */
|
||||
p = strjoin(assignment, "=", secure_getenv(assignment));
|
||||
}
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
return strv_env_replace_consume(l, p);
|
||||
}
|
||||
|
||||
int strv_env_assign(char ***l, const char *key, const char *value) {
|
||||
if (!env_name_is_valid(key))
|
||||
return -EINVAL;
|
||||
|
|
@ -577,6 +615,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
|||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
nest--;
|
||||
} else if (*e == ':') {
|
||||
if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
|
||||
len = e - word - 2;
|
||||
|
|
@ -832,3 +871,20 @@ int getenv_path_list(const char *name, char ***ret_paths) {
|
|||
*ret_paths = TAKE_PTR(l);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unsetenv_erase(const char *name) {
|
||||
char *p;
|
||||
|
||||
assert(name);
|
||||
|
||||
p = getenv(name);
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
string_erase(p);
|
||||
|
||||
if (unsetenv(name) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,15 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha
|
|||
bool strv_env_name_is_valid(char **l);
|
||||
bool strv_env_name_or_assignment_is_valid(char **l);
|
||||
|
||||
char **strv_env_merge(size_t n_lists, ...);
|
||||
char** _strv_env_merge(char **first, ...);
|
||||
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
|
||||
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
|
||||
|
||||
char **strv_env_unset(char **l, const char *p); /* In place ... */
|
||||
char **strv_env_unset_many(char **l, ...) _sentinel_;
|
||||
int strv_env_replace_consume(char ***l, char *p); /* In place ... */
|
||||
int strv_env_replace_strdup(char ***l, const char *assignment);
|
||||
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
|
||||
int strv_env_assign(char ***l, const char *key, const char *value);
|
||||
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
|
||||
|
|
@ -66,3 +68,5 @@ int setenv_systemd_exec_pid(bool update_only);
|
|||
/* Parses and does sanity checks on an environment variable containing
|
||||
* PATH-like colon-separated absolute paths */
|
||||
int getenv_path_list(const char *name, char ***ret_paths);
|
||||
|
||||
int unsetenv_erase(const char *name);
|
||||
|
|
|
|||
|
|
@ -433,11 +433,9 @@ bool fdname_is_valid(const char *s) {
|
|||
}
|
||||
|
||||
int fd_get_path(int fd, char **ret) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
int r;
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
r = readlink_malloc(procfs_path, ret);
|
||||
r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
|
||||
if (r == -ENOENT) {
|
||||
/* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
|
||||
* things debuggable and distinguish the two. */
|
||||
|
|
@ -647,7 +645,6 @@ finish:
|
|||
}
|
||||
|
||||
int fd_reopen(int fd, int flags) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
int new_fd;
|
||||
|
||||
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
|
||||
|
|
@ -657,8 +654,7 @@ int fd_reopen(int fd, int flags) {
|
|||
*
|
||||
* This implicitly resets the file read index to 0. */
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
new_fd = open(procfs_path, flags);
|
||||
new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
|
||||
if (new_fd < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <sys/socket.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "stdio-util.h"
|
||||
|
||||
/* maximum length of fdname */
|
||||
#define FDNAME_MAX 255
|
||||
|
|
@ -104,7 +105,20 @@ static inline int make_null_stdio(void) {
|
|||
0; \
|
||||
})
|
||||
|
||||
|
||||
int fd_reopen(int fd, int flags);
|
||||
int read_nr_open(void);
|
||||
int btrfs_defrag_fd(int fd);
|
||||
|
||||
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
|
||||
#define PROC_FD_PATH_MAX \
|
||||
(STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
|
||||
|
||||
static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
|
||||
assert(buf);
|
||||
assert(fd >= 0);
|
||||
assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define FORMAT_PROC_FD_PATH(fd) \
|
||||
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
|
|
@ -25,19 +26,22 @@
|
|||
#include "socket-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "sync-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
|
||||
#define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U)
|
||||
|
||||
/* The maximum size of virtual files we'll read in one go in read_virtual_file() (4M). Note that this limit
|
||||
* is different (and much lower) than the READ_FULL_BYTES_MAX limit. This reflects the fact that we use
|
||||
* different strategies for reading virtual and regular files: virtual files are generally size constrained:
|
||||
* there we allocate the full buffer size in advance. Regular files OTOH can be much larger, and here we grow
|
||||
* the allocations exponentially in a loop. In glibc large allocations are immediately backed by mmap()
|
||||
* making them relatively slow (measurably so). Thus, when allocating the full buffer in advance the large
|
||||
* limit is a problem. When allocating piecemeal it's not. Hence pick two distinct limits. */
|
||||
#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 1U)
|
||||
/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go
|
||||
* in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX
|
||||
* limit. This reflects the fact that we use different strategies for reading virtual and regular files:
|
||||
* virtual files we generally have to read in a single read() syscall since the kernel doesn't support
|
||||
* continuation read()s for them. Thankfully they are somewhat size constrained. Thus we can allocate the
|
||||
* full potential buffer in advance. Regular files OTOH can be much larger, and there we grow the allocations
|
||||
* exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/
|
||||
* allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we
|
||||
* can detect EOFs. */
|
||||
#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U)
|
||||
|
||||
int fopen_unlocked(const char *path, const char *options, FILE **ret) {
|
||||
assert(ret);
|
||||
|
|
@ -146,6 +150,30 @@ int write_string_stream_ts(
|
|||
return -EBADF;
|
||||
}
|
||||
|
||||
if (flags & WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
/* If value to be written is same as that of the existing value, then suppress the write. */
|
||||
|
||||
if (fd < 0) {
|
||||
fd = fileno(f);
|
||||
if (fd < 0)
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
/* Read an additional byte to detect cases where the prefix matches but the rest
|
||||
* doesn't. Also, 0 returned by read_virtual_file_fd() means the read was truncated and
|
||||
* 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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
|
||||
|
||||
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
|
||||
|
|
@ -261,10 +289,11 @@ int write_string_file_ts(
|
|||
assert(!ts);
|
||||
|
||||
/* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
|
||||
fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY |
|
||||
fd = open(fn, O_CLOEXEC|O_NOCTTY |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0),
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
|
||||
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
|
|
@ -373,9 +402,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
size_t n, size;
|
||||
int n_retries;
|
||||
bool truncated = false;
|
||||
|
|
@ -393,10 +421,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
|
|||
* contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
|
||||
* partial success, 1 if untruncated contents were read. */
|
||||
|
||||
fd = open(filename, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
|
||||
|
||||
/* Limit the number of attempts to read the number of bytes returned by fstat(). */
|
||||
|
|
@ -431,6 +456,11 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
|
|||
}
|
||||
|
||||
n_retries--;
|
||||
} else if (n_retries > 1) {
|
||||
/* Files in /proc are generally smaller than the page size so let's start with
|
||||
* a page size buffer from malloc and only use the max buffer on the final try. */
|
||||
size = MIN3(page_size() - 1, READ_VIRTUAL_BYTES_MAX, max_size);
|
||||
n_retries = 1;
|
||||
} else {
|
||||
size = MIN(READ_VIRTUAL_BYTES_MAX, max_size);
|
||||
n_retries = 0;
|
||||
|
|
@ -463,9 +493,14 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
|
|||
if (n <= size)
|
||||
break;
|
||||
|
||||
/* If a maximum size is specified and we already read as much, no need to try again */
|
||||
if (max_size != SIZE_MAX && n >= max_size) {
|
||||
n = max_size;
|
||||
/* If a maximum size is specified and we already read more we know the file is larger, and
|
||||
* can handle this as truncation case. Note that if the size of what we read equals the
|
||||
* maximum size then this doesn't mean truncation, the file might or might not end on that
|
||||
* byte. We need to rerun the loop in that case, with a larger buffer size, so that we read
|
||||
* at least one more byte to be able to distinguish EOF from truncation. */
|
||||
if (max_size != SIZE_MAX && n > max_size) {
|
||||
n = size; /* Make sure we never use more than what we sized the buffer for (so that
|
||||
* we have one free byte in it for the trailing NUL we add below).*/
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -512,6 +547,18 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents
|
|||
return !truncated;
|
||||
}
|
||||
|
||||
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(filename);
|
||||
|
||||
fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
|
||||
}
|
||||
|
||||
int read_full_stream_full(
|
||||
FILE *f,
|
||||
const char *filename,
|
||||
|
|
@ -721,8 +768,6 @@ int read_full_file_full(
|
|||
if (dir_fd == AT_FDCWD)
|
||||
r = sockaddr_un_set_path(&sa.un, filename);
|
||||
else {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
|
||||
/* If we shall operate relative to some directory, then let's use O_PATH first to
|
||||
* open the socket inode, and then connect to it via /proc/self/fd/. We have to do
|
||||
* this since there's not connectat() that takes a directory fd as first arg. */
|
||||
|
|
@ -731,8 +776,7 @@ int read_full_file_full(
|
|||
if (dfd < 0)
|
||||
return -errno;
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
|
||||
r = sockaddr_un_set_path(&sa.un, procfs_path);
|
||||
r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
@ -897,10 +941,12 @@ DIR *xopendirat(int fd, const char *name, int flags) {
|
|||
return d;
|
||||
}
|
||||
|
||||
static int mode_to_flags(const char *mode) {
|
||||
int fopen_mode_to_flags(const char *mode) {
|
||||
const char *p;
|
||||
int flags;
|
||||
|
||||
assert(mode);
|
||||
|
||||
if ((p = startswith(mode, "r+")))
|
||||
flags = O_RDWR;
|
||||
else if ((p = startswith(mode, "r")))
|
||||
|
|
@ -953,7 +999,7 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
|
|||
} else {
|
||||
int fd, mode_flags;
|
||||
|
||||
mode_flags = mode_to_flags(mode);
|
||||
mode_flags = fopen_mode_to_flags(mode);
|
||||
if (mode_flags < 0)
|
||||
return mode_flags;
|
||||
|
||||
|
|
@ -1093,39 +1139,6 @@ int search_and_fopen_nulstr(
|
|||
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
|
||||
}
|
||||
|
||||
int chase_symlinks_and_fopen_unlocked(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
const char *open_flags,
|
||||
FILE **ret_file,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *final_path = NULL;
|
||||
int mode_flags, r;
|
||||
|
||||
assert(path);
|
||||
assert(open_flags);
|
||||
assert(ret_file);
|
||||
|
||||
mode_flags = mode_to_flags(open_flags);
|
||||
if (mode_flags < 0)
|
||||
return mode_flags;
|
||||
|
||||
fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = take_fdopen_unlocked(&fd, open_flags, ret_file);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(final_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fflush_and_check(FILE *f) {
|
||||
assert(f);
|
||||
|
||||
|
|
@ -1153,10 +1166,7 @@ int fflush_sync_and_check(FILE *f) {
|
|||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
if (fsync(fd) < 0)
|
||||
return -errno;
|
||||
|
||||
r = fsync_directory_of_file(fd);
|
||||
r = fsync_full(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
@ -1425,16 +1435,3 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
|
|||
filename, st->st_mode & 07777);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rename_and_apply_smack_floor_label(const char *from, const char *to) {
|
||||
int r = 0;
|
||||
if (rename(from, to) < 0)
|
||||
return -errno;
|
||||
|
||||
#if HAVE_SMACK_RUN_LABEL
|
||||
r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
|
@ -15,17 +15,18 @@
|
|||
#define LONG_LINE_MAX (1U*1024U*1024U)
|
||||
|
||||
typedef enum {
|
||||
WRITE_STRING_FILE_CREATE = 1 << 0,
|
||||
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
|
||||
WRITE_STRING_FILE_ATOMIC = 1 << 2,
|
||||
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
|
||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
|
||||
WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
|
||||
WRITE_STRING_FILE_SYNC = 1 << 6,
|
||||
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7,
|
||||
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
|
||||
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
|
||||
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
|
||||
WRITE_STRING_FILE_CREATE = 1 << 0,
|
||||
WRITE_STRING_FILE_TRUNCATE = 1 << 1,
|
||||
WRITE_STRING_FILE_ATOMIC = 1 << 2,
|
||||
WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3,
|
||||
WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4,
|
||||
WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5,
|
||||
WRITE_STRING_FILE_SYNC = 1 << 6,
|
||||
WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7,
|
||||
WRITE_STRING_FILE_NOFOLLOW = 1 << 8,
|
||||
WRITE_STRING_FILE_MKDIR_0755 = 1 << 9,
|
||||
WRITE_STRING_FILE_MODE_0600 = 1 << 10,
|
||||
WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11,
|
||||
|
||||
/* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one
|
||||
more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file()
|
||||
|
|
@ -67,6 +68,7 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
|
|||
return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_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);
|
||||
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);
|
||||
|
|
@ -89,14 +91,6 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
|
|||
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path);
|
||||
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path);
|
||||
|
||||
int chase_symlinks_and_fopen_unlocked(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
const char *open_flags,
|
||||
FILE **ret_file,
|
||||
char **ret_path);
|
||||
|
||||
int fflush_and_check(FILE *f);
|
||||
int fflush_sync_and_check(FILE *f);
|
||||
|
||||
|
|
@ -125,4 +119,4 @@ int safe_fgetc(FILE *f, char *ret);
|
|||
|
||||
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
|
||||
|
||||
int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path);
|
||||
int fopen_mode_to_flags(const char *mode);
|
||||
|
|
|
|||
|
|
@ -3,23 +3,43 @@
|
|||
#include "format-util.h"
|
||||
#include "memory-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "strxcpyx.h"
|
||||
|
||||
assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
|
||||
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
|
||||
if (ifindex <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1);
|
||||
char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) {
|
||||
/* Buffer is always cleared */
|
||||
memzero(buf, IF_NAMESIZE + 1);
|
||||
if (if_indextoname(ifindex, buf))
|
||||
return buf;
|
||||
return 0;
|
||||
|
||||
if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
|
||||
return NULL;
|
||||
return -errno;
|
||||
|
||||
if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
|
||||
snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
|
||||
assert(snprintf_ok(buf, IF_NAMESIZE, "%%%d", ifindex));
|
||||
else
|
||||
snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
|
||||
assert(snprintf_ok(buf, IF_NAMESIZE, "%d", ifindex));
|
||||
|
||||
return buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) {
|
||||
char buf[IF_NAMESIZE], *copy;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = format_ifname_full(ifindex, flag, buf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
copy = strdup(buf);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = copy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
|
||||
|
|
@ -56,23 +76,23 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
|
|||
for (size_t i = 0; i < n; i++)
|
||||
if (t >= table[i].factor) {
|
||||
if (flag & FORMAT_BYTES_BELOW_POINT) {
|
||||
snprintf(buf, l,
|
||||
"%" PRIu64 ".%" PRIu64 "%s",
|
||||
t / table[i].factor,
|
||||
i != n - 1 ?
|
||||
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
|
||||
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
|
||||
table[i].suffix);
|
||||
(void) snprintf(buf, l,
|
||||
"%" PRIu64 ".%" PRIu64 "%s",
|
||||
t / table[i].factor,
|
||||
i != n - 1 ?
|
||||
(t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
|
||||
(t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
|
||||
table[i].suffix);
|
||||
} else
|
||||
snprintf(buf, l,
|
||||
"%" PRIu64 "%s",
|
||||
t / table[i].factor,
|
||||
table[i].suffix);
|
||||
(void) snprintf(buf, l,
|
||||
"%" PRIu64 "%s",
|
||||
t / table[i].factor,
|
||||
table[i].suffix);
|
||||
|
||||
goto finish;
|
||||
}
|
||||
|
||||
snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
|
||||
(void) snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
|
||||
|
||||
finish:
|
||||
buf[l-1] = 0;
|
||||
|
|
|
|||
|
|
@ -61,10 +61,23 @@ typedef enum {
|
|||
FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
|
||||
} FormatIfnameFlag;
|
||||
|
||||
char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag);
|
||||
static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
|
||||
return format_ifname_full(ifindex, buf, 0);
|
||||
int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]);
|
||||
int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret);
|
||||
|
||||
static inline int format_ifname(int ifindex, char buf[static IF_NAMESIZE]) {
|
||||
return format_ifname_full(ifindex, 0, buf);
|
||||
}
|
||||
static inline int format_ifname_alloc(int ifindex, char **ret) {
|
||||
return format_ifname_full_alloc(ifindex, 0, ret);
|
||||
}
|
||||
|
||||
static inline char *_format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) {
|
||||
(void) format_ifname_full(ifindex, flag, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define FORMAT_IFNAME_FULL(index, flag) _format_ifname_full(index, flag, (char[IF_NAMESIZE]){})
|
||||
#define FORMAT_IFNAME(index) _format_ifname_full(index, 0, (char[IF_NAMESIZE]){})
|
||||
|
||||
typedef enum {
|
||||
FORMAT_BYTES_USE_IEC = 1 << 0,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_fcntl.h"
|
||||
|
|
@ -45,50 +44,39 @@ int unlink_noerrno(const char *path) {
|
|||
}
|
||||
|
||||
int rmdir_parents(const char *path, const char *stop) {
|
||||
size_t l;
|
||||
int r = 0;
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(stop);
|
||||
|
||||
l = strlen(path);
|
||||
if (!path_is_safe(path))
|
||||
return -EINVAL;
|
||||
|
||||
/* Skip trailing slashes */
|
||||
while (l > 0 && path[l-1] == '/')
|
||||
l--;
|
||||
if (!path_is_safe(stop))
|
||||
return -EINVAL;
|
||||
|
||||
while (l > 0) {
|
||||
char *t;
|
||||
p = strdupa(path);
|
||||
|
||||
/* Skip last component */
|
||||
while (l > 0 && path[l-1] != '/')
|
||||
l--;
|
||||
for (;;) {
|
||||
char *slash = NULL;
|
||||
|
||||
/* Skip trailing slashes */
|
||||
while (l > 0 && path[l-1] == '/')
|
||||
l--;
|
||||
|
||||
if (l <= 0)
|
||||
break;
|
||||
|
||||
t = strndup(path, l);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
if (path_startswith(stop, t)) {
|
||||
free(t);
|
||||
/* skip the last component. */
|
||||
r = path_find_last_component(p, /* accept_dot_dot= */ false, (const char **) &slash, NULL);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
if (slash == p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = rmdir(t);
|
||||
free(t);
|
||||
assert(*slash == '/');
|
||||
*slash = '\0';
|
||||
|
||||
if (r < 0)
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
if (path_startswith_full(stop, p, /* accept_dot_dot= */ false))
|
||||
return 0;
|
||||
|
||||
if (rmdir(p) < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
|
||||
|
|
@ -308,14 +296,11 @@ int fchmod_umask(int fd, mode_t m) {
|
|||
}
|
||||
|
||||
int fchmod_opath(int fd, mode_t m) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
|
||||
/* This function operates also on fd that might have been opened with
|
||||
* O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
|
||||
* fchownat() does. */
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
if (chmod(procfs_path, m) < 0) {
|
||||
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -329,12 +314,9 @@ int fchmod_opath(int fd, mode_t m) {
|
|||
}
|
||||
|
||||
int futimens_opath(int fd, const struct timespec ts[2]) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
|
||||
/* Similar to fchmod_path() but for futimens() */
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
|
||||
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -380,9 +362,8 @@ int fd_warn_permissions(const char *path, int fd) {
|
|||
}
|
||||
|
||||
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
|
||||
char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r, ret = 0;
|
||||
int r, ret;
|
||||
|
||||
assert(path);
|
||||
|
||||
|
|
@ -412,8 +393,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
|
|||
/* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
|
||||
* ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
|
||||
* something fchown(), fchmod(), futimensat() don't allow. */
|
||||
xsprintf(fdpath, "/proc/self/fd/%i", fd);
|
||||
|
||||
ret = fchmod_and_chown(fd, mode, uid, gid);
|
||||
|
||||
if (stamp != USEC_INFINITY) {
|
||||
|
|
@ -421,11 +400,11 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
|
|||
|
||||
timespec_store(&ts[0], stamp);
|
||||
ts[1] = ts[0];
|
||||
r = utimensat(AT_FDCWD, fdpath, ts, 0);
|
||||
r = futimens_opath(fd, ts);
|
||||
} else
|
||||
r = utimensat(AT_FDCWD, fdpath, NULL, 0);
|
||||
r = futimens_opath(fd, NULL);
|
||||
if (r < 0 && ret >= 0)
|
||||
return -errno;
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -702,547 +681,10 @@ int unlink_or_warn(const char *filename) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
||||
char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
|
||||
int wd;
|
||||
|
||||
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
|
||||
xsprintf(path, "/proc/self/fd/%i", what);
|
||||
|
||||
wd = inotify_add_watch(fd, path, mask);
|
||||
if (wd < 0)
|
||||
return -errno;
|
||||
|
||||
return wd;
|
||||
}
|
||||
|
||||
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
|
||||
int wd;
|
||||
|
||||
wd = inotify_add_watch(fd, pathname, mask);
|
||||
if (wd < 0) {
|
||||
if (errno == ENOSPC)
|
||||
return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname);
|
||||
|
||||
return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname);
|
||||
}
|
||||
|
||||
return wd;
|
||||
}
|
||||
|
||||
bool unsafe_transition(const struct stat *a, const struct stat *b) {
|
||||
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
|
||||
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
|
||||
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
|
||||
|
||||
if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
|
||||
return false;
|
||||
|
||||
return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
|
||||
}
|
||||
|
||||
static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
|
||||
_cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (!FLAGS_SET(flags, CHASE_WARN))
|
||||
return -ENOLINK;
|
||||
|
||||
(void) fd_get_path(a, &n1);
|
||||
(void) fd_get_path(b, &n2);
|
||||
|
||||
if (fstat(a, &st) == 0)
|
||||
user_a = uid_to_name(st.st_uid);
|
||||
if (fstat(b, &st) == 0)
|
||||
user_b = uid_to_name(st.st_uid);
|
||||
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
|
||||
"Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.",
|
||||
strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path);
|
||||
}
|
||||
|
||||
static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
|
||||
_cleanup_free_ char *n1 = NULL;
|
||||
|
||||
if (!FLAGS_SET(flags, CHASE_WARN))
|
||||
return -EREMOTE;
|
||||
|
||||
(void) fd_get_path(fd, &n1);
|
||||
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
|
||||
"Detected autofs mount point %s during canonicalization of %s.",
|
||||
strna(n1), path);
|
||||
}
|
||||
|
||||
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {
|
||||
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
|
||||
bool exists = true, append_trail_slash = false;
|
||||
struct stat previous_stat;
|
||||
const char *todo;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Either the file may be missing, or we return an fd to the final object, but both make no sense */
|
||||
if ((flags & CHASE_NONEXISTENT) && ret_fd)
|
||||
return -EINVAL;
|
||||
|
||||
if ((flags & CHASE_STEP) && ret_fd)
|
||||
return -EINVAL;
|
||||
|
||||
if (isempty(path))
|
||||
return -EINVAL;
|
||||
|
||||
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
|
||||
* symlinks relative to a root directory, instead of the root of the host.
|
||||
*
|
||||
* Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
|
||||
* relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
|
||||
* assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
|
||||
* prefixed accordingly.
|
||||
*
|
||||
* Algorithmically this operates on two path buffers: "done" are the components of the path we already
|
||||
* processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
|
||||
* process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
|
||||
* each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
|
||||
* slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
|
||||
* to a minimum.
|
||||
*
|
||||
* Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
|
||||
* as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
|
||||
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
|
||||
* specified path.
|
||||
*
|
||||
* There are five ways to invoke this function:
|
||||
*
|
||||
* 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is
|
||||
* returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0
|
||||
* is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is
|
||||
* returned if the destination was found, -ENOENT if it wasn't.
|
||||
*
|
||||
* 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file
|
||||
* descriptor is returned as return value. This is useful to open files relative to some root
|
||||
* directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using
|
||||
* fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with
|
||||
* CHASE_NONEXISTENT.
|
||||
*
|
||||
* 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first
|
||||
* symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if
|
||||
* a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the
|
||||
* path is fully normalized, and == 0 for each normalization step. This may be combined with
|
||||
* CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
|
||||
*
|
||||
* 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
|
||||
* unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
|
||||
* CHASE_WARN is also set, a warning describing the unsafe transition is emitted.
|
||||
*
|
||||
* 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization
|
||||
* is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of
|
||||
* the mount point is emitted.
|
||||
*/
|
||||
|
||||
/* A root directory of "/" or "" is identical to none */
|
||||
if (empty_or_root(original_root))
|
||||
original_root = NULL;
|
||||
|
||||
if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) {
|
||||
/* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set
|
||||
* and doesn't care about any of the other special features we provide either. */
|
||||
r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_fd = r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (original_root) {
|
||||
r = path_make_absolute_cwd(original_root, &root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Simplify the root directory, so that it has no duplicate slashes and nothing at the
|
||||
* end. While we won't resolve the root path we still simplify it. Note that dropping the
|
||||
* trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY
|
||||
* anyway. Moreover at the end of this function after processing everything we'll always turn
|
||||
* the empty string back to "/". */
|
||||
delete_trailing_chars(root, "/");
|
||||
path_simplify(root);
|
||||
|
||||
if (flags & CHASE_PREFIX_ROOT) {
|
||||
/* We don't support relative paths in combination with a root directory */
|
||||
if (!path_is_absolute(path))
|
||||
return -EINVAL;
|
||||
|
||||
path = prefix_roota(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
r = path_make_absolute_cwd(path, &buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE)
|
||||
if (fstat(fd, &previous_stat) < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_TRAIL_SLASH)
|
||||
append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/.");
|
||||
|
||||
if (root) {
|
||||
/* If we are operating on a root directory, let's take the root directory as it is. */
|
||||
|
||||
todo = path_startswith(buffer, root);
|
||||
if (!todo)
|
||||
return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG,
|
||||
SYNTHETIC_ERRNO(ECHRNG),
|
||||
"Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
|
||||
path, root);
|
||||
|
||||
done = strdup(root);
|
||||
} else {
|
||||
todo = buffer;
|
||||
done = strdup("/");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *first = NULL;
|
||||
_cleanup_close_ int child = -1;
|
||||
struct stat st;
|
||||
const char *e;
|
||||
|
||||
r = path_find_first_component(&todo, true, &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) { /* We reached the end. */
|
||||
if (append_trail_slash)
|
||||
if (!strextend(&done, "/"))
|
||||
return -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
first = strndup(e, r);
|
||||
if (!first)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Two dots? Then chop off the last bit of what we already found out. */
|
||||
if (path_equal(first, "..")) {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
_cleanup_close_ int fd_parent = -1;
|
||||
|
||||
/* If we already are at the top, then going up will not change anything. This is in-line with
|
||||
* how the kernel handles this. */
|
||||
if (empty_or_root(done))
|
||||
continue;
|
||||
|
||||
parent = dirname_malloc(done);
|
||||
if (!parent)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Don't allow this to leave the root dir. */
|
||||
if (root &&
|
||||
path_startswith(done, root) &&
|
||||
!path_startswith(parent, root))
|
||||
continue;
|
||||
|
||||
free_and_replace(done, parent);
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
goto chased_one;
|
||||
|
||||
fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
if (fd_parent < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE) {
|
||||
if (fstat(fd_parent, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (unsafe_transition(&previous_stat, &st))
|
||||
return log_unsafe_transition(fd, fd_parent, path, flags);
|
||||
|
||||
previous_stat = st;
|
||||
}
|
||||
|
||||
safe_close(fd);
|
||||
fd = TAKE_FD(fd_parent);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise let's see what this is. */
|
||||
child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH);
|
||||
if (child < 0) {
|
||||
if (errno == ENOENT &&
|
||||
(flags & CHASE_NONEXISTENT) &&
|
||||
(isempty(todo) || path_is_safe(todo))) {
|
||||
/* If CHASE_NONEXISTENT is set, and the path does not exist, then
|
||||
* that's OK, return what we got so far. But don't allow this if the
|
||||
* remaining path contains "../" or something else weird. */
|
||||
|
||||
if (!path_extend(&done, first, todo))
|
||||
return -ENOMEM;
|
||||
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (fstat(child, &st) < 0)
|
||||
return -errno;
|
||||
if ((flags & CHASE_SAFE) &&
|
||||
unsafe_transition(&previous_stat, &st))
|
||||
return log_unsafe_transition(fd, child, path, flags);
|
||||
|
||||
previous_stat = st;
|
||||
|
||||
if ((flags & CHASE_NO_AUTOFS) &&
|
||||
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
|
||||
return log_autofs_mount_point(child, path, flags);
|
||||
|
||||
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
|
||||
_cleanup_free_ char *destination = NULL;
|
||||
|
||||
/* This is a symlink, in this case read the destination. But let's make sure we
|
||||
* don't follow symlinks without bounds. */
|
||||
if (--max_follow <= 0)
|
||||
return -ELOOP;
|
||||
|
||||
r = readlinkat_malloc(fd, first, &destination);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (isempty(destination))
|
||||
return -EINVAL;
|
||||
|
||||
if (path_is_absolute(destination)) {
|
||||
|
||||
/* An absolute destination. Start the loop from the beginning, but use the root
|
||||
* directory as base. */
|
||||
|
||||
safe_close(fd);
|
||||
fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (flags & CHASE_SAFE) {
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (unsafe_transition(&previous_stat, &st))
|
||||
return log_unsafe_transition(child, fd, path, flags);
|
||||
|
||||
previous_stat = st;
|
||||
}
|
||||
|
||||
/* Note that we do not revalidate the root, we take it as is. */
|
||||
r = free_and_strdup(&done, empty_to_root(root));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Prefix what's left to do with what we just read, and start the loop again, but
|
||||
* remain in the current directory. */
|
||||
if (!path_extend(&destination, todo))
|
||||
return -ENOMEM;
|
||||
|
||||
free_and_replace(buffer, destination);
|
||||
todo = buffer;
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
goto chased_one;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this is not a symlink, then let's just add the name we read to what we already verified. */
|
||||
if (!path_extend(&done, first))
|
||||
return -ENOMEM;
|
||||
|
||||
/* And iterate again, but go one directory further down. */
|
||||
safe_close(fd);
|
||||
fd = TAKE_FD(child);
|
||||
}
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(done);
|
||||
|
||||
if (ret_fd) {
|
||||
/* Return the O_PATH fd we currently are looking to the caller. It can translate it to a
|
||||
* proper fd by opening /proc/self/fd/xyz. */
|
||||
|
||||
assert(fd >= 0);
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
}
|
||||
|
||||
if (flags & CHASE_STEP)
|
||||
return 1;
|
||||
|
||||
return exists;
|
||||
|
||||
chased_one:
|
||||
if (ret_path) {
|
||||
const char *e;
|
||||
|
||||
/* todo may contain slashes at the beginning. */
|
||||
r = path_find_first_component(&todo, true, &e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
*ret_path = TAKE_PTR(done);
|
||||
else {
|
||||
char *c;
|
||||
|
||||
c = path_join(done, e);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_path = c;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_open(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
int open_flags,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_close_ int path_fd = -1;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
if (chase_flags & CHASE_NONEXISTENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
|
||||
/* Shortcut this call if none of the special features of this call are requested */
|
||||
r = open(path, open_flags);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(path_fd >= 0);
|
||||
|
||||
r = fd_reopen(path_fd, open_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_opendir(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
char **ret_path,
|
||||
DIR **ret_dir) {
|
||||
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int path_fd = -1;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
DIR *d;
|
||||
int r;
|
||||
|
||||
if (!ret_dir)
|
||||
return -EINVAL;
|
||||
if (chase_flags & CHASE_NONEXISTENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
|
||||
/* Shortcut this call if none of the special features of this call are requested */
|
||||
d = opendir(path);
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
*ret_dir = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(path_fd >= 0);
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
|
||||
d = opendir(procfs_path);
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
*ret_dir = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_stat(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
char **ret_path,
|
||||
struct stat *ret_stat,
|
||||
int *ret_fd) {
|
||||
|
||||
_cleanup_close_ int path_fd = -1;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret_stat);
|
||||
|
||||
if (chase_flags & CHASE_NONEXISTENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
|
||||
/* Shortcut this call if none of the special features of this call are requested */
|
||||
if (stat(path, ret_stat) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(path_fd >= 0);
|
||||
|
||||
if (fstat(path_fd, ret_stat) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(path_fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int access_fd(int fd, int mode) {
|
||||
char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
|
||||
|
||||
/* Like access() but operates on an already open fd */
|
||||
|
||||
xsprintf(p, "/proc/self/fd/%i", fd);
|
||||
if (access(p, mode) < 0) {
|
||||
if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
|
|
@ -1377,172 +819,6 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int fsync_directory_of_file(int fd) {
|
||||
_cleanup_close_ int dfd = -1;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check
|
||||
* for the inode type first */
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
|
||||
if (dfd < 0)
|
||||
return -errno;
|
||||
|
||||
} else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other
|
||||
* types check O_PATH flag */
|
||||
int flags;
|
||||
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0)
|
||||
return -errno;
|
||||
|
||||
if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case
|
||||
* we can sensibly do what is requested. Otherwise this refers
|
||||
* to a socket, fifo or device node, where the concept of a
|
||||
* containing directory doesn't make too much sense. */
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
if (dfd < 0) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
r = fd_get_path(fd, &path);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
|
||||
fd,
|
||||
r == -ENOSYS ? ", ignoring" : "");
|
||||
|
||||
if (r == -ENOSYS)
|
||||
/* If /proc is not available, we're most likely running in some
|
||||
* chroot environment, and syncing the directory is not very
|
||||
* important in that case. Let's just silently do nothing. */
|
||||
return 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(path))
|
||||
return -EINVAL;
|
||||
|
||||
dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
|
||||
if (dfd < 0)
|
||||
return dfd;
|
||||
}
|
||||
|
||||
if (fsync(dfd) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsync_full(int fd) {
|
||||
int r, q;
|
||||
|
||||
/* Sync both the file and the directory */
|
||||
|
||||
r = fsync(fd) < 0 ? -errno : 0;
|
||||
|
||||
q = fsync_directory_of_file(fd);
|
||||
if (r < 0) /* Return earlier error */
|
||||
return r;
|
||||
if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a
|
||||
* parent dir */
|
||||
return 0;
|
||||
return q;
|
||||
}
|
||||
|
||||
int fsync_path_at(int at_fd, const char *path) {
|
||||
_cleanup_close_ int opened_fd = -1;
|
||||
int fd;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (at_fd == AT_FDCWD) {
|
||||
opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
} else
|
||||
fd = at_fd;
|
||||
} else {
|
||||
opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
}
|
||||
|
||||
if (fsync(fd) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fsync_parent_at(int at_fd, const char *path) {
|
||||
_cleanup_close_ int opened_fd = -1;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (at_fd != AT_FDCWD)
|
||||
return fsync_directory_of_file(at_fd);
|
||||
|
||||
opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fsync(opened_fd) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fsync_directory_of_file(opened_fd);
|
||||
}
|
||||
|
||||
int fsync_path_and_parent_at(int at_fd, const char *path) {
|
||||
_cleanup_close_ int opened_fd = -1;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (at_fd != AT_FDCWD)
|
||||
return fsync_full(at_fd);
|
||||
|
||||
opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
} else
|
||||
opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fsync_full(opened_fd);
|
||||
}
|
||||
|
||||
int syncfs_path(int atfd, const char *path) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (atfd != AT_FDCWD)
|
||||
return syncfs(atfd) < 0 ? -errno : 0;
|
||||
|
||||
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
} else
|
||||
fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (syncfs(fd) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_parent(const char *path, int flags, mode_t mode) {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
int fd, r;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -67,44 +66,6 @@ int var_tmp_dir(const char **ret);
|
|||
|
||||
int unlink_or_warn(const char *filename);
|
||||
|
||||
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
|
||||
|
||||
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
|
||||
for ((e) = &buffer.ev; \
|
||||
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
|
||||
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
|
||||
|
||||
union inotify_event_buffer {
|
||||
struct inotify_event ev;
|
||||
uint8_t raw[INOTIFY_EVENT_MAX];
|
||||
};
|
||||
|
||||
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
|
||||
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
|
||||
|
||||
enum {
|
||||
CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
|
||||
CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
|
||||
CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
|
||||
CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
|
||||
CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
|
||||
CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
|
||||
CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
|
||||
* right-most component refers to symlink, return O_PATH fd of the symlink. */
|
||||
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */
|
||||
};
|
||||
|
||||
bool unsafe_transition(const struct stat *a, const struct stat *b);
|
||||
|
||||
/* How many iterations to execute before returning -ELOOP */
|
||||
#define CHASE_SYMLINKS_MAX 32
|
||||
|
||||
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd);
|
||||
|
||||
int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
|
||||
int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
|
||||
int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
|
||||
|
||||
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
|
||||
static inline char *rmdir_and_free(char *p) {
|
||||
PROTECT_ERRNO;
|
||||
|
|
@ -137,14 +98,6 @@ typedef enum UnlinkDeallocateFlags {
|
|||
|
||||
int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
|
||||
|
||||
int fsync_directory_of_file(int fd);
|
||||
int fsync_full(int fd);
|
||||
int fsync_path_at(int at_fd, const char *path);
|
||||
int fsync_parent_at(int at_fd, const char *path);
|
||||
int fsync_path_and_parent_at(int at_fd, const char *path);
|
||||
|
||||
int syncfs_path(int atfd, const char *path);
|
||||
|
||||
int open_parent(const char *path, int flags, mode_t mode);
|
||||
|
||||
int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_
|
|||
if (idx < _IDX_SWAP_END)
|
||||
return &bucket_at_swap(swap, idx)->p.b;
|
||||
|
||||
assert_not_reached("Invalid index");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
|
||||
|
|
@ -513,7 +513,7 @@ static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
|
|||
return (void*) e->key;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown hashmap type");
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1747,7 +1747,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) {
|
|||
r = set_merge((Set*)copy, (Set*)h);
|
||||
break;
|
||||
default:
|
||||
assert_not_reached("Unknown hashmap type");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
|
|
|
|||
|
|
@ -36,66 +36,39 @@ char* get_default_hostname(void) {
|
|||
return strdup(FALLBACK_HOSTNAME);
|
||||
}
|
||||
|
||||
char* gethostname_malloc(void) {
|
||||
int gethostname_full(GetHostnameFlags flags, char **ret) {
|
||||
_cleanup_free_ char *buf = NULL, *fallback = NULL;
|
||||
struct utsname u;
|
||||
const char *s;
|
||||
|
||||
/* This call tries to return something useful, either the actual hostname
|
||||
* or it makes something up. The only reason it might fail is OOM.
|
||||
* It might even return "localhost" if that's set. */
|
||||
assert(ret);
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
|
||||
s = u.nodename;
|
||||
if (isempty(s) || streq(s, "(none)"))
|
||||
return get_default_hostname();
|
||||
if (isempty(s) ||
|
||||
(!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && 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))
|
||||
return -ENXIO;
|
||||
|
||||
return strdup(s);
|
||||
}
|
||||
|
||||
char* gethostname_short_malloc(void) {
|
||||
struct utsname u;
|
||||
const char *s;
|
||||
_cleanup_free_ char *f = NULL;
|
||||
|
||||
/* Like above, but kills the FQDN part if present. */
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
|
||||
s = u.nodename;
|
||||
if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
|
||||
s = f = get_default_hostname();
|
||||
s = fallback = get_default_hostname();
|
||||
if (!s)
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
|
||||
assert(s[0] != '.');
|
||||
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return strndup(s, strcspn(s, "."));
|
||||
}
|
||||
|
||||
int gethostname_strict(char **ret) {
|
||||
struct utsname u;
|
||||
char *k;
|
||||
|
||||
/* This call will rather fail than make up a name. It will not return "localhost" either. */
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
|
||||
if (isempty(u.nodename))
|
||||
return -ENXIO;
|
||||
|
||||
if (streq(u.nodename, "(none)"))
|
||||
return -ENXIO;
|
||||
|
||||
if (is_localhost(u.nodename))
|
||||
return -ENXIO;
|
||||
|
||||
k = strdup(u.nodename);
|
||||
if (!k)
|
||||
if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
|
||||
buf = strndup(s, strcspn(s, "."));
|
||||
else
|
||||
buf = strdup(s);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = k;
|
||||
*ret = TAKE_PTR(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,41 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "env-file.h"
|
||||
#include "macro.h"
|
||||
#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. */
|
||||
} GetHostnameFlags;
|
||||
|
||||
int gethostname_full(GetHostnameFlags flags, char **ret);
|
||||
static inline int gethostname_strict(char **ret) {
|
||||
return gethostname_full(0, ret);
|
||||
}
|
||||
|
||||
static inline char* gethostname_malloc(void) {
|
||||
char *s;
|
||||
|
||||
if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
|
||||
return NULL;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline char* gethostname_short_malloc(void) {
|
||||
char *s;
|
||||
|
||||
if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
|
||||
return NULL;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
char* get_default_hostname(void);
|
||||
char* gethostname_malloc(void);
|
||||
char* gethostname_short_malloc(void);
|
||||
int gethostname_strict(char **ret);
|
||||
|
||||
bool valid_ldh_char(char c) _const_;
|
||||
|
||||
|
|
@ -33,3 +61,7 @@ static inline bool is_outbound_hostname(const char *hostname) {
|
|||
/* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */
|
||||
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
|
||||
}
|
||||
|
||||
static inline int get_pretty_hostname(char **ret) {
|
||||
return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -794,7 +794,7 @@ int in_addr_prefix_from_string_auto_internal(
|
|||
k = 0;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached("Invalid prefixlen mode");
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (ret_family)
|
||||
|
|
@ -831,35 +831,6 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
|
|||
|
||||
DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
|
||||
|
||||
static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) {
|
||||
assert(a);
|
||||
assert(state);
|
||||
|
||||
siphash24_compress(&a->family, sizeof(a->family), state);
|
||||
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
|
||||
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
|
||||
}
|
||||
|
||||
static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) {
|
||||
int r;
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
r = CMP(x->family, y->family);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = CMP(x->prefixlen, y->prefixlen);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func);
|
||||
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free);
|
||||
|
||||
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
|
||||
assert(addr);
|
||||
assert(state);
|
||||
|
|
|
|||
|
|
@ -20,12 +20,6 @@ struct in_addr_data {
|
|||
union in_addr_union address;
|
||||
};
|
||||
|
||||
struct in_addr_prefix {
|
||||
int family;
|
||||
uint8_t prefixlen;
|
||||
union in_addr_union address;
|
||||
};
|
||||
|
||||
bool in4_addr_is_null(const struct in_addr *a);
|
||||
static inline bool in4_addr_is_set(const struct in_addr *a) {
|
||||
return !in4_addr_is_null(a);
|
||||
|
|
@ -124,8 +118,6 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
|
|||
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
|
||||
|
||||
extern const struct hash_ops in_addr_data_hash_ops;
|
||||
extern const struct hash_ops in_addr_prefix_hash_ops;
|
||||
extern const struct hash_ops in_addr_prefix_hash_ops_free;
|
||||
extern const struct hash_ops in6_addr_hash_ops;
|
||||
|
||||
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
|
||||
|
|
|
|||
29
src/libnm-systemd-shared/src/basic/inotify-util.c
Normal file
29
src/libnm-systemd-shared/src/basic/inotify-util.c
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "inotify-util.h"
|
||||
|
||||
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
||||
int wd;
|
||||
|
||||
/* 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;
|
||||
|
||||
return wd;
|
||||
}
|
||||
|
||||
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
|
||||
int wd;
|
||||
|
||||
wd = inotify_add_watch(fd, pathname, mask);
|
||||
if (wd < 0) {
|
||||
if (errno == ENOSPC)
|
||||
return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname);
|
||||
|
||||
return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname);
|
||||
}
|
||||
|
||||
return wd;
|
||||
}
|
||||
22
src/libnm-systemd-shared/src/basic/inotify-util.h
Normal file
22
src/libnm-systemd-shared/src/basic/inotify-util.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
|
||||
|
||||
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
|
||||
for ((e) = &buffer.ev; \
|
||||
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
|
||||
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
|
||||
|
||||
union inotify_event_buffer {
|
||||
struct inotify_event ev;
|
||||
uint8_t raw[INOTIFY_EVENT_MAX];
|
||||
};
|
||||
|
||||
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
|
||||
int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
|
||||
|
|
@ -142,11 +142,8 @@
|
|||
#define LIST_FOREACH_SAFE(name,i,n,head) \
|
||||
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
|
||||
|
||||
#define LIST_FOREACH_BEFORE(name,i,p) \
|
||||
for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
|
||||
|
||||
#define LIST_FOREACH_AFTER(name,i,p) \
|
||||
for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
|
||||
#define LIST_FOREACH_BACKWARDS(name,i,p) \
|
||||
for ((i) = (p); (i); (i) = (i)->name##_prev)
|
||||
|
||||
/* Iterate through all the members of the list p is included in, but skip over p */
|
||||
#define LIST_FOREACH_OTHERS(name,i,p) \
|
||||
|
|
|
|||
|
|
@ -32,6 +32,15 @@ typedef enum LogTarget{
|
|||
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
|
||||
#define ERRNO_VALUE(val) (abs(val) & 255)
|
||||
|
||||
/* The callback function to be invoked when syntax warnings are seen
|
||||
* in the unit files. */
|
||||
typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
|
||||
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
|
||||
|
||||
static inline void clear_log_syntax_callback(dummy_t *dummy) {
|
||||
set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
|
||||
}
|
||||
|
||||
const char *log_target_to_string(LogTarget target) _const_;
|
||||
LogTarget log_target_from_string(const char *s) _pure_;
|
||||
void log_set_target(LogTarget target);
|
||||
|
|
@ -174,7 +183,6 @@ _noreturn_ void log_assert_failed(
|
|||
const char *func);
|
||||
|
||||
_noreturn_ void log_assert_failed_unreachable(
|
||||
const char *text,
|
||||
const char *file,
|
||||
int line,
|
||||
const char *func);
|
||||
|
|
|
|||
|
|
@ -20,31 +20,14 @@
|
|||
#define _sentinel_ __attribute__((__sentinel__))
|
||||
#define _destructor_ __attribute__((__destructor__))
|
||||
#define _deprecated_ __attribute__((__deprecated__))
|
||||
#define _packed_ __attribute__((__packed__))
|
||||
#define _malloc_ __attribute__((__malloc__))
|
||||
#define _weak_ __attribute__((__weak__))
|
||||
#define _likely_(x) (__builtin_expect(!!(x), 1))
|
||||
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
|
||||
#define _public_ __attribute__((__visibility__("default")))
|
||||
#define _hidden_ __attribute__((__visibility__("hidden")))
|
||||
#define _weakref_(x) __attribute__((__weakref__(#x)))
|
||||
#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
|
||||
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
|
||||
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
|
||||
#if __GNUC__ >= 7
|
||||
#define _fallthrough_ __attribute__((__fallthrough__))
|
||||
#else
|
||||
#define _fallthrough_
|
||||
#endif
|
||||
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
|
||||
* compiler versions */
|
||||
#ifndef _noreturn_
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define _noreturn_ _Noreturn
|
||||
#else
|
||||
#define _noreturn_ __attribute__((__noreturn__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
|
||||
# if defined(__has_feature)
|
||||
|
|
@ -163,6 +146,20 @@
|
|||
#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 == ULONGLONG_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));
|
||||
}
|
||||
|
||||
|
|
@ -209,13 +206,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
|
|||
return m;
|
||||
}
|
||||
|
||||
/*
|
||||
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
|
||||
* Contrary to strlen(), this is a constant expression.
|
||||
* @x: a string literal.
|
||||
*/
|
||||
#define STRLEN(x) ((unsigned) sizeof(""x"") - 1)
|
||||
|
||||
/*
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
|
|
@ -282,8 +272,8 @@ static inline int __coverity_check_and_return__(int condition) {
|
|||
#define assert(expr) assert_message_se(expr, #expr)
|
||||
#endif
|
||||
|
||||
#define assert_not_reached(t) \
|
||||
log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
|
||||
#define assert_not_reached() \
|
||||
log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
|
||||
|
||||
#define assert_return(expr, r) \
|
||||
do { \
|
||||
|
|
@ -345,12 +335,12 @@ static inline int __coverity_check_and_return__(int condition) {
|
|||
(2U+(sizeof(type) <= 1 ? 3U : \
|
||||
sizeof(type) <= 2 ? 5U : \
|
||||
sizeof(type) <= 4 ? 10U : \
|
||||
sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
|
||||
#define DECIMAL_STR_WIDTH(x) \
|
||||
({ \
|
||||
typeof(x) _x_ = (x); \
|
||||
unsigned ans = 1; \
|
||||
size_t ans = 1; \
|
||||
while ((_x_ /= 10) != 0) \
|
||||
ans++; \
|
||||
ans; \
|
||||
|
|
@ -483,4 +473,10 @@ static inline size_t size_add(size_t x, size_t y) {
|
|||
return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int _empty[0];
|
||||
} dummy_t;
|
||||
|
||||
assert_cc(sizeof(dummy_t) == 0);
|
||||
|
||||
#include "log.h"
|
||||
|
|
|
|||
|
|
@ -18,26 +18,25 @@ size_t page_size(void) {
|
|||
return pgsz;
|
||||
}
|
||||
|
||||
bool memeqzero(const void *data, size_t length) {
|
||||
/* Does the buffer consist entirely of NULs?
|
||||
bool memeqbyte(uint8_t byte, const void *data, size_t length) {
|
||||
/* Does the buffer consist entirely of the same specific byte value?
|
||||
* Copied from https://github.com/systemd/casync/, copied in turn from
|
||||
* https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
|
||||
* which is licensed CC-0.
|
||||
*/
|
||||
|
||||
const uint8_t *p = data;
|
||||
size_t i;
|
||||
|
||||
/* Check first 16 bytes manually */
|
||||
for (i = 0; i < 16; i++, length--) {
|
||||
for (size_t i = 0; i < 16; i++, length--) {
|
||||
if (length == 0)
|
||||
return true;
|
||||
if (p[i])
|
||||
if (p[i] != byte)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now we know first 16 bytes are NUL, memcmp with self. */
|
||||
return memcmp(data, p + i, length) == 0;
|
||||
/* Now we know first 16 bytes match, memcmp() with self. */
|
||||
return memcmp(data, p + 16, length) == 0;
|
||||
}
|
||||
|
||||
#if !HAVE_EXPLICIT_BZERO
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
|
|||
|
||||
#define zero(x) (memzero(&(x), sizeof(x)))
|
||||
|
||||
bool memeqzero(const void *data, size_t length);
|
||||
bool memeqbyte(uint8_t byte, const void *data, size_t length);
|
||||
|
||||
#define memeqzero(data, length) memeqbyte(0x00, data, length)
|
||||
|
||||
#define eqzero(x) memeqzero(x, sizeof(x))
|
||||
|
||||
|
|
@ -71,6 +73,16 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
|
|||
return memmem(haystack, haystacklen, needle, needlelen);
|
||||
}
|
||||
|
||||
static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
|
||||
const uint8_t *p;
|
||||
|
||||
p = memmem_safe(haystack, haystacklen, needle, needlelen);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
return (uint8_t*) p + needlelen;
|
||||
}
|
||||
|
||||
#if HAVE_EXPLICIT_BZERO
|
||||
static inline void* explicit_bzero_safe(void *p, size_t l) {
|
||||
if (l > 0)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,26 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old)
|
|||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_IOPRIO_GET
|
||||
static inline int missing_ioprio_get(int which, int who) {
|
||||
return syscall(__NR_ioprio_get, which, who);
|
||||
}
|
||||
|
||||
# define ioprio_get missing_ioprio_get
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_IOPRIO_SET
|
||||
static inline int missing_ioprio_set(int which, int who, int ioprio) {
|
||||
return syscall(__NR_ioprio_set, which, who, ioprio);
|
||||
}
|
||||
|
||||
# define ioprio_set missing_ioprio_set
|
||||
#endif
|
||||
|
||||
/* ======================================================================= */
|
||||
|
||||
#if !HAVE_MEMFD_CREATE
|
||||
static inline int missing_memfd_create(const char *name, unsigned int flags) {
|
||||
# ifdef __NR_memfd_create
|
||||
|
|
|
|||
|
|
@ -740,13 +740,13 @@ int parse_oom_score_adjust(const char *s, int *ret) {
|
|||
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
|
||||
assert(ret);
|
||||
|
||||
if (i >= (~0UL << FSHIFT))
|
||||
if (i >= (~0UL << LOADAVG_PRECISION_BITS))
|
||||
return -ERANGE;
|
||||
|
||||
i = i << FSHIFT;
|
||||
f = DIV_ROUND_UP((f << FSHIFT), 100);
|
||||
i = i << LOADAVG_PRECISION_BITS;
|
||||
f = DIV_ROUND_UP((f << LOADAVG_PRECISION_BITS), 100);
|
||||
|
||||
if (f >= FIXED_1)
|
||||
if (f >= LOADAVG_FIXED_POINT_1_0)
|
||||
return -ERANGE;
|
||||
|
||||
*ret = i | f;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <linux/loadavg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
|
@ -136,6 +135,14 @@ int parse_ip_prefix_length(const char *s, int *ret);
|
|||
|
||||
int parse_oom_score_adjust(const char *s, int *ret);
|
||||
|
||||
/* Implement floating point using fixed integers, to improve performance when
|
||||
* calculating load averages. These macros can be used to extract the integer
|
||||
* and decimal parts of a value. */
|
||||
#define LOADAVG_PRECISION_BITS 11
|
||||
#define LOADAVG_FIXED_POINT_1_0 (1 << LOADAVG_PRECISION_BITS)
|
||||
#define LOADAVG_INT_SIDE(x) ((x) >> LOADAVG_PRECISION_BITS)
|
||||
#define LOADAVG_DECIMAL_SIDE(x) LOADAVG_INT_SIDE(((x) & (LOADAVG_FIXED_POINT_1_0 - 1)) * 100)
|
||||
|
||||
/* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it
|
||||
* to a loadavg_t. */
|
||||
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#undef basename
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
|
|
@ -630,7 +631,11 @@ static int check_x_access(const char *path, int *ret_fd) {
|
|||
return r;
|
||||
|
||||
r = access_fd(fd, X_OK);
|
||||
if (r < 0)
|
||||
if (r == -ENOSYS) {
|
||||
/* /proc is not mounted. Fallback to access(). */
|
||||
if (access(path, X_OK) < 0)
|
||||
return -errno;
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_fd)
|
||||
|
|
@ -639,30 +644,53 @@ static int check_x_access(const char *path, int *ret_fd) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
|
||||
int last_error, r;
|
||||
static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
_cleanup_free_ char *path_name = NULL;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
/* Function chase_symlinks() is invoked only when root is not NULL, as using it regardless of
|
||||
* root value would alter the behavior of existing callers for example: /bin/sleep would become
|
||||
* /usr/bin/sleep when find_executables is called. Hence, this function should be invoked when
|
||||
* needed to avoid unforeseen regression or other complicated changes. */
|
||||
if (root) {
|
||||
r = chase_symlinks(name,
|
||||
root,
|
||||
CHASE_PREFIX_ROOT,
|
||||
&path_name,
|
||||
/* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
name = path_name;
|
||||
}
|
||||
|
||||
r = check_x_access(name, ret_fd ? &fd : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_filename) {
|
||||
r = path_make_absolute_cwd(name, ret_filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
|
||||
int last_error = -ENOENT, r = 0;
|
||||
const char *p = NULL;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (is_path(name)) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
r = check_x_access(name, ret_fd ? &fd : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_filename) {
|
||||
r = path_make_absolute_cwd(name, ret_filename);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (is_path(name))
|
||||
return find_executable_impl(name, root, ret_filename, ret_fd);
|
||||
|
||||
if (use_path_envvar)
|
||||
/* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
|
||||
|
|
@ -671,12 +699,31 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
|
|||
if (!p)
|
||||
p = DEFAULT_PATH;
|
||||
|
||||
last_error = -ENOENT;
|
||||
if (exec_search_path) {
|
||||
char **element;
|
||||
|
||||
STRV_FOREACH(element, exec_search_path) {
|
||||
_cleanup_free_ char *full_path = NULL;
|
||||
if (!path_is_absolute(*element))
|
||||
continue;
|
||||
full_path = path_join(*element, name);
|
||||
if (!full_path)
|
||||
return -ENOMEM;
|
||||
|
||||
r = find_executable_impl(full_path, root, ret_filename, ret_fd);
|
||||
if (r < 0) {
|
||||
if (r != -EACCES)
|
||||
last_error = r;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return last_error;
|
||||
}
|
||||
|
||||
/* Resolve a single-component name to a full path */
|
||||
for (;;) {
|
||||
_cleanup_free_ char *element = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
|
|
@ -690,7 +737,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
|
|||
if (!path_extend(&element, name))
|
||||
return -ENOMEM;
|
||||
|
||||
r = check_x_access(element, ret_fd ? &fd : NULL);
|
||||
r = find_executable_impl(element, root, ret_filename, ret_fd);
|
||||
if (r < 0) {
|
||||
/* PATH entries which we don't have access to are ignored, as per tradition. */
|
||||
if (r != -EACCES)
|
||||
|
|
@ -699,11 +746,6 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
|
|||
}
|
||||
|
||||
/* Found it! */
|
||||
if (ret_filename)
|
||||
*ret_filename = path_simplify(TAKE_PTR(element));
|
||||
if (ret_fd)
|
||||
*ret_fd = TAKE_FD(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ int path_strv_make_absolute_cwd(char **l);
|
|||
char** path_strv_resolve(char **l, const char *root);
|
||||
char** path_strv_resolve_uniq(char **l, const char *root);
|
||||
|
||||
int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd);
|
||||
int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd);
|
||||
static inline int find_executable(const char *name, char **ret_filename) {
|
||||
return find_executable_full(name, true, ret_filename, NULL);
|
||||
return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL);
|
||||
}
|
||||
|
||||
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
|
||||
|
|
|
|||
|
|
@ -173,6 +173,16 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx) {
|
||||
int r;
|
||||
|
||||
r = prioq_ensure_allocated(q, compare_func);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return prioq_put(*q, data, idx);
|
||||
}
|
||||
|
||||
static void remove_item(Prioq *q, struct prioq_item *i) {
|
||||
struct prioq_item *l;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free);
|
|||
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func);
|
||||
|
||||
int prioq_put(Prioq *q, void *data, unsigned *idx);
|
||||
int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx);
|
||||
int prioq_remove(Prioq *q, void *data, unsigned *idx);
|
||||
int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
|
|
@ -1254,7 +1253,7 @@ int safe_fork_full(
|
|||
|
||||
pid_t original_pid, pid;
|
||||
sigset_t saved_ss, ss;
|
||||
_cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
|
||||
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
|
||||
bool block_signals = false, block_all = false;
|
||||
int prio, r;
|
||||
|
||||
|
|
@ -1503,6 +1502,24 @@ int set_oom_score_adjust(int value) {
|
|||
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
}
|
||||
|
||||
int get_oom_score_adjust(int *ret) {
|
||||
_cleanup_free_ char *t;
|
||||
int r, a;
|
||||
|
||||
r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
delete_trailing_chars(t, WHITESPACE);
|
||||
|
||||
assert_se(safe_atoi(t, &a) >= 0);
|
||||
assert_se(oom_score_adjust_is_valid(a));
|
||||
|
||||
if (ret)
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
#include "alloc-util.h"
|
||||
#include "format-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "macro.h"
|
||||
#include "missing_ioprio.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define procfs_file_alloca(pid, field) \
|
||||
|
|
@ -175,6 +175,7 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *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 set_oom_score_adjust(int value);
|
||||
int get_oom_score_adjust(int *ret);
|
||||
|
||||
/* The highest possibly (theoretic) pid_t value on this architecture. */
|
||||
#define PID_T_MAX ((pid_t) INT32_MAX)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ static inline Set* set_free_free(Set *s) {
|
|||
|
||||
/* no set_free_free_free */
|
||||
|
||||
#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS))
|
||||
#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s) HASHMAP_DEBUG_SRC_ARGS))
|
||||
|
||||
int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
|
||||
#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ int pop_pending_signal_internal(int sig, ...) {
|
|||
if (sigemptyset(&ss) < 0)
|
||||
return -errno;
|
||||
|
||||
/* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build
|
||||
/* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build
|
||||
* parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
|
||||
* this.) */
|
||||
if (sig > 0 && sigaddset(&ss, sig) < 0)
|
||||
|
|
|
|||
|
|
@ -455,23 +455,23 @@ int sockaddr_pretty(
|
|||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1];
|
||||
char a[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
|
||||
if (sa->in6.sin6_scope_id != 0)
|
||||
format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX);
|
||||
|
||||
if (include_port) {
|
||||
r = asprintf(&p,
|
||||
if (asprintf(&p,
|
||||
"[%s]:%u%s%s",
|
||||
a,
|
||||
be16toh(sa->in6.sin6_port),
|
||||
sa->in6.sin6_scope_id != 0 ? "%" : "",
|
||||
sa->in6.sin6_scope_id != 0 ? ifname : "");
|
||||
if (r < 0)
|
||||
FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX)) < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a);
|
||||
if (sa->in6.sin6_scope_id != 0)
|
||||
p = strjoin(a, "%", FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX));
|
||||
else
|
||||
p = strdup(a);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -793,7 +793,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
|
|||
|
||||
/* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl
|
||||
* directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */
|
||||
if (STR_IN_SET(p, "all", "default"))
|
||||
if (!FLAGS_SET(flags, IFNAME_VALID_SPECIAL) && STR_IN_SET(p, "all", "default"))
|
||||
return false;
|
||||
|
||||
for (const char *t = p; *t; t++) {
|
||||
|
|
@ -921,7 +921,7 @@ int getpeergroups(int fd, gid_t **ret) {
|
|||
ssize_t send_one_fd_iov_sa(
|
||||
int transport_fd,
|
||||
int fd,
|
||||
struct iovec *iov, size_t iovlen,
|
||||
const struct iovec *iov, size_t iovlen,
|
||||
const struct sockaddr *sa, socklen_t len,
|
||||
int flags) {
|
||||
|
||||
|
|
@ -929,7 +929,7 @@ ssize_t send_one_fd_iov_sa(
|
|||
struct msghdr mh = {
|
||||
.msg_name = (struct sockaddr*) sa,
|
||||
.msg_namelen = len,
|
||||
.msg_iov = iov,
|
||||
.msg_iov = (struct iovec *)iov,
|
||||
.msg_iovlen = iovlen,
|
||||
};
|
||||
ssize_t k;
|
||||
|
|
@ -1233,7 +1233,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
|
|||
}
|
||||
|
||||
int socket_bind_to_ifindex(int fd, int ifindex) {
|
||||
char ifname[IF_NAMESIZE + 1];
|
||||
char ifname[IF_NAMESIZE];
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
|
@ -1251,8 +1251,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
|
|||
return r;
|
||||
|
||||
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
|
||||
if (!format_ifname(ifindex, ifname))
|
||||
return -errno;
|
||||
r = format_ifname(ifindex, ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return socket_bind_to_ifname(fd, ifname);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,9 +135,10 @@ int ip_tos_to_string_alloc(int i, char **s);
|
|||
int ip_tos_from_string(const char *s);
|
||||
|
||||
typedef enum {
|
||||
IFNAME_VALID_ALTERNATIVE = 1 << 0,
|
||||
IFNAME_VALID_NUMERIC = 1 << 1,
|
||||
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
|
||||
IFNAME_VALID_ALTERNATIVE = 1 << 0, /* Allow "altnames" too */
|
||||
IFNAME_VALID_NUMERIC = 1 << 1, /* Allow decimal formatted ifindexes too */
|
||||
IFNAME_VALID_SPECIAL = 1 << 2, /* Allow the special names "all" and "default" */
|
||||
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC | IFNAME_VALID_SPECIAL,
|
||||
} IfnameValidFlags;
|
||||
bool ifname_valid_char(char a);
|
||||
bool ifname_valid_full(const char *p, IfnameValidFlags flags);
|
||||
|
|
@ -153,7 +154,7 @@ int getpeergroups(int fd, gid_t **ret);
|
|||
ssize_t send_one_fd_iov_sa(
|
||||
int transport_fd,
|
||||
int fd,
|
||||
struct iovec *iov, size_t iovlen,
|
||||
const struct iovec *iov, size_t iovlen,
|
||||
const struct sockaddr *sa, socklen_t len,
|
||||
int flags);
|
||||
int send_one_fd_sa(int transport_fd,
|
||||
|
|
@ -245,7 +246,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
|
|||
_len = sizeof(struct sockaddr_vm); \
|
||||
break; \
|
||||
default: \
|
||||
assert_not_reached("invalid socket family"); \
|
||||
assert_not_reached(); \
|
||||
} \
|
||||
_len; \
|
||||
})
|
||||
|
|
@ -277,6 +278,28 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
|
|||
int socket_bind_to_ifname(int fd, const char *ifname);
|
||||
int socket_bind_to_ifindex(int fd, int ifindex);
|
||||
|
||||
/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
|
||||
struct timeval_large {
|
||||
uint64_t tvl_sec, tvl_usec;
|
||||
};
|
||||
struct timespec_large {
|
||||
uint64_t tvl_sec, tvl_nsec;
|
||||
};
|
||||
|
||||
/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
|
||||
* See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them
|
||||
* to prevent from recvmsg_safe() returning -EXFULL. */
|
||||
#define CMSG_SPACE_TIMEVAL \
|
||||
((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \
|
||||
CMSG_SPACE(sizeof(struct timeval)) : \
|
||||
CMSG_SPACE(sizeof(struct timeval)) + \
|
||||
CMSG_SPACE(sizeof(struct timeval_large)))
|
||||
#define CMSG_SPACE_TIMESPEC \
|
||||
((sizeof(struct timespec) == sizeof(struct timespec_large)) ? \
|
||||
CMSG_SPACE(sizeof(struct timespec)) : \
|
||||
CMSG_SPACE(sizeof(struct timespec)) + \
|
||||
CMSG_SPACE(sizeof(struct timespec_large)))
|
||||
|
||||
ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
|
||||
|
||||
int socket_get_family(int fd, int *ret);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "dirent-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing_fs.h"
|
||||
#include "missing_magic.h"
|
||||
|
|
|
|||
|
|
@ -9,8 +9,13 @@
|
|||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
|
||||
#define snprintf_ok(buf, len, fmt, ...) \
|
||||
((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
|
||||
#define snprintf_ok(buf, len, fmt, ...) \
|
||||
({ \
|
||||
char *_buf = (buf); \
|
||||
size_t _len = (len); \
|
||||
int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \
|
||||
_snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \
|
||||
})
|
||||
|
||||
#define xsprintf(buf, fmt, ...) \
|
||||
assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
|
||||
|
|
@ -58,7 +63,7 @@ do { \
|
|||
(void) va_arg(ap, long double); \
|
||||
break; \
|
||||
default: \
|
||||
assert_not_reached("Unknown format string argument."); \
|
||||
assert_not_reached(); \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
|
|
|||
|
|
@ -1146,3 +1146,19 @@ int string_contains_word_strv(const char *string, const char *separators, char *
|
|||
*ret_word = found;
|
||||
return !!found;
|
||||
}
|
||||
|
||||
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
|
||||
if (!s1 && !s2)
|
||||
return true;
|
||||
if (!s1 || !s2)
|
||||
return false;
|
||||
|
||||
if (!ok)
|
||||
ok = WHITESPACE;
|
||||
|
||||
for (; *s1 && *s2; s1++, s2++)
|
||||
if (*s1 != *s2)
|
||||
break;
|
||||
|
||||
return in_charset(s1, ok) && in_charset(s2, ok);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
|
|||
}
|
||||
REENABLE_WARNING;
|
||||
|
||||
/* Like startswith(), but operates on arbitrary memory blocks */
|
||||
static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
|
||||
assert(token);
|
||||
|
||||
size_t n = strlen(token);
|
||||
if (sz < n)
|
||||
return NULL;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (memcmp(p, token, n) != 0)
|
||||
return NULL;
|
||||
|
||||
return (uint8_t*) p + n;
|
||||
}
|
||||
|
||||
/* Like startswith_no_case(), but operates on arbitrary memory blocks.
|
||||
* It works only for ASCII strings.
|
||||
*/
|
||||
|
|
@ -242,3 +226,5 @@ int string_contains_word_strv(const char *string, const char *separators, char *
|
|||
static inline int string_contains_word(const char *string, const char *separators, const char *word) {
|
||||
return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
|
||||
}
|
||||
|
||||
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
|
||||
|
|
|
|||
|
|
@ -300,6 +300,24 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
|
|||
return (int) n;
|
||||
}
|
||||
|
||||
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(s);
|
||||
|
||||
r = strv_split_full(&l, s, separators, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv(t, l, filter_duplicates);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return (int) strv_length(*t);
|
||||
}
|
||||
|
||||
int strv_split_colon_pairs(char ***t, const char *s) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
size_t n = 0;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ static inline char **strv_split(const char *s, const char *separators) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags);
|
||||
#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) {
|
||||
char **ret;
|
||||
|
|
@ -233,9 +236,11 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
|
|||
|
||||
#define strv_free_and_replace(a, b) \
|
||||
({ \
|
||||
strv_free(a); \
|
||||
(a) = (b); \
|
||||
(b) = NULL; \
|
||||
char ***_a = &(a); \
|
||||
char ***_b = &(b); \
|
||||
strv_free(*_a); \
|
||||
(*_a) = (*_b); \
|
||||
(*_b) = NULL; \
|
||||
0; \
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -433,62 +433,62 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
|
|||
usec_t years = d / USEC_PER_YEAR;
|
||||
usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
|
||||
|
||||
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
years,
|
||||
years == 1 ? "year" : "years",
|
||||
months,
|
||||
months == 1 ? "month" : "months",
|
||||
s);
|
||||
(void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
years,
|
||||
years == 1 ? "year" : "years",
|
||||
months,
|
||||
months == 1 ? "month" : "months",
|
||||
s);
|
||||
} else if (d >= USEC_PER_MONTH) {
|
||||
usec_t months = d / USEC_PER_MONTH;
|
||||
usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
|
||||
|
||||
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
months,
|
||||
months == 1 ? "month" : "months",
|
||||
days,
|
||||
days == 1 ? "day" : "days",
|
||||
s);
|
||||
(void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
months,
|
||||
months == 1 ? "month" : "months",
|
||||
days,
|
||||
days == 1 ? "day" : "days",
|
||||
s);
|
||||
} else if (d >= USEC_PER_WEEK) {
|
||||
usec_t weeks = d / USEC_PER_WEEK;
|
||||
usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
|
||||
|
||||
snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
weeks,
|
||||
weeks == 1 ? "week" : "weeks",
|
||||
days,
|
||||
days == 1 ? "day" : "days",
|
||||
s);
|
||||
(void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
|
||||
weeks,
|
||||
weeks == 1 ? "week" : "weeks",
|
||||
days,
|
||||
days == 1 ? "day" : "days",
|
||||
s);
|
||||
} else if (d >= 2*USEC_PER_DAY)
|
||||
snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
|
||||
(void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
|
||||
else if (d >= 25*USEC_PER_HOUR)
|
||||
snprintf(buf, l, "1 day " USEC_FMT "h %s",
|
||||
(d - USEC_PER_DAY) / USEC_PER_HOUR, s);
|
||||
(void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
|
||||
(d - USEC_PER_DAY) / USEC_PER_HOUR, s);
|
||||
else if (d >= 6*USEC_PER_HOUR)
|
||||
snprintf(buf, l, USEC_FMT "h %s",
|
||||
d / USEC_PER_HOUR, s);
|
||||
(void) snprintf(buf, l, USEC_FMT "h %s",
|
||||
d / USEC_PER_HOUR, s);
|
||||
else if (d >= USEC_PER_HOUR)
|
||||
snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
|
||||
d / USEC_PER_HOUR,
|
||||
(d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
|
||||
(void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
|
||||
d / USEC_PER_HOUR,
|
||||
(d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
|
||||
else if (d >= 5*USEC_PER_MINUTE)
|
||||
snprintf(buf, l, USEC_FMT "min %s",
|
||||
d / USEC_PER_MINUTE, s);
|
||||
(void) snprintf(buf, l, USEC_FMT "min %s",
|
||||
d / USEC_PER_MINUTE, s);
|
||||
else if (d >= USEC_PER_MINUTE)
|
||||
snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
|
||||
d / USEC_PER_MINUTE,
|
||||
(d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
|
||||
(void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
|
||||
d / USEC_PER_MINUTE,
|
||||
(d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
|
||||
else if (d >= USEC_PER_SEC)
|
||||
snprintf(buf, l, USEC_FMT "s %s",
|
||||
d / USEC_PER_SEC, s);
|
||||
(void) snprintf(buf, l, USEC_FMT "s %s",
|
||||
d / USEC_PER_SEC, s);
|
||||
else if (d >= USEC_PER_MSEC)
|
||||
snprintf(buf, l, USEC_FMT "ms %s",
|
||||
d / USEC_PER_MSEC, s);
|
||||
(void) snprintf(buf, l, USEC_FMT "ms %s",
|
||||
d / USEC_PER_MSEC, s);
|
||||
else if (d > 0)
|
||||
snprintf(buf, l, USEC_FMT"us %s",
|
||||
d, s);
|
||||
(void) snprintf(buf, l, USEC_FMT"us %s",
|
||||
d, s);
|
||||
else
|
||||
snprintf(buf, l, "now");
|
||||
(void) snprintf(buf, l, "now");
|
||||
|
||||
buf[l-1] = 0;
|
||||
return buf;
|
||||
|
|
|
|||
|
|
@ -300,11 +300,7 @@ int link_tmpfile(int fd, const char *path, const char *target) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
|
||||
|
||||
xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
|
||||
|
||||
if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
|
||||
if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,26 @@
|
|||
#define _const_ __attribute__((__const__))
|
||||
#define _pure_ __attribute__((__pure__))
|
||||
#define _section_(x) __attribute__((__section__(x)))
|
||||
#define _packed_ __attribute__((__packed__))
|
||||
#define _used_ __attribute__((__used__))
|
||||
#define _unused_ __attribute__((__unused__))
|
||||
#define _cleanup_(x) __attribute__((__cleanup__(x)))
|
||||
#define _likely_(x) (__builtin_expect(!!(x), 1))
|
||||
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
|
||||
#if __GNUC__ >= 7
|
||||
#define _fallthrough_ __attribute__((__fallthrough__))
|
||||
#else
|
||||
#define _fallthrough_
|
||||
#endif
|
||||
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
|
||||
* compiler versions */
|
||||
#ifndef _noreturn_
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define _noreturn_ _Noreturn
|
||||
#else
|
||||
#define _noreturn_ __attribute__((__noreturn__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define XSTRINGIFY(x) #x
|
||||
#define STRINGIFY(x) XSTRINGIFY(x)
|
||||
|
|
@ -34,7 +51,14 @@
|
|||
#define CONCATENATE(x, y) XCONCATENATE(x, y)
|
||||
|
||||
#ifdef SD_BOOT
|
||||
#define assert(expr) do {} while (false)
|
||||
#ifdef NDEBUG
|
||||
#define assert(expr)
|
||||
#define assert_not_reached() __builtin_unreachable()
|
||||
#else
|
||||
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
|
||||
#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
|
||||
#endif
|
||||
|
||||
#if defined(static_assert)
|
||||
|
|
@ -70,12 +94,29 @@
|
|||
UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
|
||||
})
|
||||
|
||||
/* evaluates to (void) if _A or _B are not constant or of different types */
|
||||
#define IS_UNSIGNED_INTEGER_TYPE(type) \
|
||||
(__builtin_types_compatible_p(typeof(type), unsigned char) || \
|
||||
__builtin_types_compatible_p(typeof(type), unsigned short) || \
|
||||
__builtin_types_compatible_p(typeof(type), unsigned) || \
|
||||
__builtin_types_compatible_p(typeof(type), unsigned long) || \
|
||||
__builtin_types_compatible_p(typeof(type), unsigned long long))
|
||||
|
||||
#define IS_SIGNED_INTEGER_TYPE(type) \
|
||||
(__builtin_types_compatible_p(typeof(type), signed char) || \
|
||||
__builtin_types_compatible_p(typeof(type), signed short) || \
|
||||
__builtin_types_compatible_p(typeof(type), signed) || \
|
||||
__builtin_types_compatible_p(typeof(type), signed long) || \
|
||||
__builtin_types_compatible_p(typeof(type), signed long long))
|
||||
|
||||
/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes
|
||||
* is also OK as long as the signedness matches) */
|
||||
#define CONST_MAX(_A, _B) \
|
||||
(__builtin_choose_expr( \
|
||||
__builtin_constant_p(_A) && \
|
||||
__builtin_constant_p(_B) && \
|
||||
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
|
||||
(__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \
|
||||
(IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \
|
||||
(IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \
|
||||
((_A) > (_B)) ? (_A) : (_B), \
|
||||
VOID_0))
|
||||
|
||||
|
|
@ -216,3 +257,10 @@
|
|||
(ptr) = NULL; \
|
||||
_ptr_; \
|
||||
})
|
||||
|
||||
/*
|
||||
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
|
||||
* Contrary to strlen(), this is a constant expression.
|
||||
* @x: a string literal.
|
||||
*/
|
||||
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#define strncmp(a, b, n) StrnCmp((a), (b), (n))
|
||||
#define strcasecmp(a, b) StriCmp((a), (b))
|
||||
#define STR_C(str) (L ## str)
|
||||
#define memcmp(a, b, n) CompareMem(a, b, n)
|
||||
#else
|
||||
#define STR_C(str) (str)
|
||||
#endif
|
||||
|
|
@ -65,3 +66,19 @@ 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) {
|
||||
assert(token);
|
||||
|
||||
sd_size_t n = strlen(token) * sizeof(sd_char);
|
||||
if (sz < n)
|
||||
return NULL;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (memcmp(p, token, n) != 0)
|
||||
return NULL;
|
||||
|
||||
return (uint8_t*) p + n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,24 +17,6 @@
|
|||
log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \
|
||||
({ \
|
||||
int _e = (error); \
|
||||
if (DEBUG_LOGGING) \
|
||||
log_interface_full_errno_zerook( \
|
||||
ifname_expr, \
|
||||
LOG_DEBUG, _e, prefix fmt, \
|
||||
##__VA_ARGS__); \
|
||||
-ERRNO_VALUE(_e); \
|
||||
})
|
||||
|
||||
#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \
|
||||
({ \
|
||||
int _error = (error); \
|
||||
ASSERT_NON_ZERO(_error); \
|
||||
log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* The following macros append INTERFACE= to the message.
|
||||
* The macros require a struct named 'Link' which contains 'char *ifname':
|
||||
|
|
|
|||
|
|
@ -36,16 +36,29 @@ bool http_url_is_valid(const char *url) {
|
|||
return ascii_is_valid(p);
|
||||
}
|
||||
|
||||
bool file_url_is_valid(const char *url) {
|
||||
const char *p;
|
||||
|
||||
if (isempty(url))
|
||||
return false;
|
||||
|
||||
p = startswith(url, "file:/");
|
||||
if (isempty(p))
|
||||
return false;
|
||||
|
||||
return ascii_is_valid(p);
|
||||
}
|
||||
|
||||
bool documentation_url_is_valid(const char *url) {
|
||||
const char *p;
|
||||
|
||||
if (isempty(url))
|
||||
return false;
|
||||
|
||||
if (http_url_is_valid(url))
|
||||
if (http_url_is_valid(url) || file_url_is_valid(url))
|
||||
return true;
|
||||
|
||||
p = STARTSWITH_SET(url, "file:/", "info:", "man:");
|
||||
p = STARTSWITH_SET(url, "info:", "man:");
|
||||
if (isempty(p))
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "macro.h"
|
||||
|
||||
bool http_url_is_valid(const char *url) _pure_;
|
||||
bool file_url_is_valid(const char *url) _pure_;
|
||||
|
||||
bool documentation_url_is_valid(const char *url) _pure_;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue