merge: branch 'ff/systemd_merge'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1866
This commit is contained in:
Fernando Fernandez Mancera 2024-02-20 11:00:19 +01:00
commit aabb4fad8e
124 changed files with 5589 additions and 1979 deletions

View file

@ -2351,7 +2351,6 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/mkdir.h \
src/libnm-systemd-shared/sd-adapt-shared/mountpoint-util.h \
src/libnm-systemd-shared/sd-adapt-shared/namespace-util.h \
src/libnm-systemd-shared/sd-adapt-shared/netif-util.h \
src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h \
src/libnm-systemd-shared/sd-adapt-shared/nulstr-util.h \
src/libnm-systemd-shared/sd-adapt-shared/os-util.h \
@ -2366,7 +2365,9 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/sd-adapt-shared/virt.h \
src/libnm-systemd-shared/src/basic/alloc-util.c \
src/libnm-systemd-shared/src/basic/alloc-util.h \
src/libnm-systemd-shared/src/basic/async.h \
src/libnm-systemd-shared/src/basic/arphrd-util.h \
src/libnm-systemd-shared/src/basic/btrfs.c \
src/libnm-systemd-shared/src/basic/btrfs.h \
src/libnm-systemd-shared/src/basic/cgroup-util.h \
src/libnm-systemd-shared/src/basic/constants.h \
src/libnm-systemd-shared/src/basic/dns-def.h \
@ -2405,6 +2406,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/inotify-util.h \
src/libnm-systemd-shared/src/basic/io-util.c \
src/libnm-systemd-shared/src/basic/io-util.h \
src/libnm-systemd-shared/src/basic/iovec-util.h \
src/libnm-systemd-shared/src/basic/label.c \
src/libnm-systemd-shared/src/basic/label.h \
src/libnm-systemd-shared/src/basic/list.h \
@ -2424,6 +2426,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/missing_syscall.h \
src/libnm-systemd-shared/src/basic/missing_threads.h \
src/libnm-systemd-shared/src/basic/missing_type.h \
src/libnm-systemd-shared/src/basic/namespace-util.h \
src/libnm-systemd-shared/src/basic/ordered-set.c \
src/libnm-systemd-shared/src/basic/ordered-set.h \
src/libnm-systemd-shared/src/basic/origin-id.h \
@ -2431,6 +2434,7 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
src/libnm-systemd-shared/src/basic/parse-util.h \
src/libnm-systemd-shared/src/basic/path-util.c \
src/libnm-systemd-shared/src/basic/path-util.h \
src/libnm-systemd-shared/src/basic/pidref.h \
src/libnm-systemd-shared/src/basic/prioq.c \
src/libnm-systemd-shared/src/basic/prioq.h \
src/libnm-systemd-shared/src/basic/process-util.c \
@ -2510,14 +2514,17 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
src/libnm-systemd-core/sd-adapt-core/condition.h \
src/libnm-systemd-core/sd-adapt-core/conf-parser.h \
src/libnm-systemd-core/sd-adapt-core/khash.h \
src/libnm-systemd-core/sd-adapt-core/netif-util.c \
src/libnm-systemd-core/sd-adapt-core/netif-util.h \
src/libnm-systemd-core/sd-adapt-core/network-util.h \
src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c \
src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h \
src/libnm-systemd-core/sd-adapt-core/sd-daemon.h \
src/libnm-systemd-core/sd-adapt-core/sd-messages.h \
src/libnm-systemd-core/sd-adapt-core/udev-util.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c \
src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h \
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c \
src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h \
src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c \
@ -2539,9 +2546,11 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c \
src/libnm-systemd-core/src/systemd/_sd-common.h \
src/libnm-systemd-core/src/systemd/sd-device.h \
src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h \
src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h \
src/libnm-systemd-core/src/systemd/sd-event.h \
src/libnm-systemd-core/src/systemd/sd-id128.h \
src/libnm-systemd-core/src/systemd/sd-ndisc.h \

View file

@ -67,6 +67,15 @@ G_DEFINE_TYPE(NMDhcpSystemd, nm_dhcp_systemd, NM_TYPE_DHCP_CLIENT)
/*****************************************************************************/
static guint32
lifetime_to_uint32(guint64 lft)
{
if (lft == G_MAXUINT64)
return G_MAXUINT32;
return lft / 1000000;
}
static NML3ConfigData *
lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GError **error)
{
@ -100,18 +109,19 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
if (!config->v6.info_only) {
gboolean has_any_addresses = FALSE;
uint32_t lft_pref;
uint32_t lft_valid;
uint64_t lft_pref;
uint64_t lft_valid;
sd_dhcp6_lease_reset_address_iter(lease);
sd_dhcp6_lease_address_iterator_reset(lease);
nm_gstring_prepare(&str);
while (sd_dhcp6_lease_get_address(lease, &tmp_addr, &lft_pref, &lft_valid) >= 0) {
const NMPlatformIP6Address address = {
while (sd_dhcp6_lease_get_address(lease, &tmp_addr) >= 0
&& sd_dhcp6_lease_get_address_lifetime(lease, &lft_pref, &lft_valid) >= 0) {
NMPlatformIP6Address address = {
.plen = 128,
.address = tmp_addr,
.timestamp = ts,
.lifetime = lft_valid,
.preferred = lft_pref,
.lifetime = lifetime_to_uint32(lft_valid),
.preferred = lifetime_to_uint32(lft_pref),
.addr_source = NM_IP_CONFIG_SOURCE_DHCP,
};
@ -121,6 +131,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
has_any_addresses = TRUE;
sd_dhcp6_lease_address_iterator_next(lease);
}
if (str->len) {
@ -160,11 +171,12 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
uint8_t prefix_len;
nm_gstring_prepare(&str);
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
while (!sd_dhcp6_lease_get_pd(lease, &prefix, &prefix_len, NULL, NULL)) {
sd_dhcp6_lease_pd_iterator_reset(lease);
while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix, &prefix_len)) {
nm_gstring_add_space_delimiter(str);
nm_inet6_ntop(&prefix, addr_str);
g_string_append_printf(str, "%s/%u", addr_str, prefix_len);
sd_dhcp6_lease_pd_iterator_next(lease);
}
if (str->len > 0) {
nm_dhcp_option_add_option(options,
@ -235,6 +247,8 @@ bound6_handle(NMDhcpSystemd *self)
gs_free_error GError *error = NULL;
NMPlatformIP6Address prefix = {0};
sd_dhcp6_lease *lease = NULL;
guint64 lft_valid;
guint64 lft_pref;
if (sd_dhcp6_client_get_lease(priv->client6, &lease) < 0 || !lease) {
_LOGW(" no lease!");
@ -254,14 +268,14 @@ bound6_handle(NMDhcpSystemd *self)
_nm_dhcp_client_notify(NM_DHCP_CLIENT(self), NM_DHCP_CLIENT_EVENT_TYPE_BOUND, l3cd);
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
while (!sd_dhcp6_lease_get_pd(lease,
&prefix.address,
&prefix.plen,
&prefix.preferred,
&prefix.lifetime)) {
sd_dhcp6_lease_pd_iterator_reset(lease);
while (!sd_dhcp6_lease_get_pd_prefix(lease, &prefix.address, &prefix.plen)
&& !sd_dhcp6_lease_get_pd_lifetime(lease, &lft_pref, &lft_valid)) {
prefix.preferred = lifetime_to_uint32(lft_pref);
prefix.lifetime = lifetime_to_uint32(lft_valid);
prefix.timestamp = ts;
nm_dhcp_client_emit_ipv6_prefix_delegated(NM_DHCP_CLIENT(self), &prefix);
sd_dhcp6_lease_pd_iterator_next(lease);
}
}
@ -339,10 +353,10 @@ ip6_start(NMDhcpClient *client, const struct in6_addr *ll_addr, GError **error)
return FALSE;
}
r = sd_dhcp6_client_set_duid(sd_client,
unaligned_read_be16(&duid_arr[0]),
&duid_arr[2],
duid_len - 2);
r = sd_dhcp6_client_set_duid_raw(sd_client,
unaligned_read_be16(&duid_arr[0]),
&duid_arr[2],
duid_len - 2);
if (r < 0) {
nm_utils_error_set_errno(error, r, "failed to set DUID: %s");
return FALSE;

View file

@ -3,11 +3,11 @@
libnm_systemd_core = static_library(
'nm-systemd-core',
sources: files(
'src/libsystemd-network/dhcp-identifier.c',
'src/libsystemd-network/dhcp6-network.c',
'src/libsystemd-network/dhcp6-option.c',
'src/libsystemd-network/dhcp6-protocol.c',
'src/libsystemd-network/network-common.c',
'src/libsystemd-network/sd-dhcp-duid.c',
'src/libsystemd-network/sd-dhcp6-client.c',
'src/libsystemd-network/sd-dhcp6-lease.c',
'src/libsystemd/sd-event/event-util.c',
@ -15,6 +15,7 @@ libnm_systemd_core = static_library(
'src/libsystemd/sd-id128/id128-util.c',
'src/libsystemd/sd-id128/sd-id128.c',
'nm-sd.c',
'sd-adapt-core/netif-util.c',
'sd-adapt-core/nm-sd-adapt-core.c',
),
include_directories: [

View file

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-core.h"
#include <linux/if.h>
#include <linux/if_arp.h>
#include "arphrd-util.h"
#include "device-util.h"
#include "log-link.h"
#include "memory-util.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "strv.h"
#if 0 /* NM_IGNORED */
bool netif_has_carrier(uint8_t operstate, unsigned flags) {
/* see Documentation/networking/operstates.txt in the kernel sources */
if (operstate == IF_OPER_UP)
return true;
if (operstate != IF_OPER_UNKNOWN)
return false;
/* operstate may not be implemented, so fall back to flags */
return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
!FLAGS_SET(flags, IFF_DORMANT);
}
int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
const char *t;
char *p;
if (device &&
sd_device_get_devtype(device, &t) >= 0 &&
!isempty(t)) {
p = strdup(t);
if (!p)
return -ENOMEM;
*ret = p;
return 0;
}
t = arphrd_to_name(iftype);
if (!t)
return -ENOENT;
p = strdup(t);
if (!p)
return -ENOMEM;
*ret = ascii_strlower(p);
return 0;
}
#endif /* NM_IGNORED */
const char *
net_get_persistent_name(sd_device *device)
{
assert(device);
/* fetch some persistent data unique (on this machine) to this device */
FOREACH_STRING(field,
"ID_NET_NAME_ONBOARD",
"ID_NET_NAME_SLOT",
"ID_NET_NAME_PATH",
"ID_NET_NAME_MAC")
{
const char *name;
if (sd_device_get_property_value(device, field, &name) >= 0)
return name;
}
return NULL;
}
#if 0 /* NM_IGNORED */
/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */
#define HASH_KEY SD_ID128_MAKE(d3, 1e, 48, fa, 90, fe, 4b, 4c, 9d, af, d5, d7, a1, b1, 2e, 8a)
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) {
const char *name;
assert(device);
assert(ret);
/* net_get_persistent_name() will return one of the device names based on stable information about
* the device. If this is not available, we fall back to using the actual device name. */
name = net_get_persistent_name(device);
if (!name && use_sysname)
(void) sd_device_get_sysname(device, &name);
if (!name)
return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
"No stable identifying information found");
log_device_debug(device, "Using \"%s\" as stable identifying information", name);
return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret);
}
int net_get_unique_predictable_data_from_name(
const char *name,
const sd_id128_t *key,
uint64_t *ret) {
size_t l, sz;
uint8_t *v;
int r;
assert(name);
assert(key);
assert(ret);
l = strlen(name);
sz = sizeof(sd_id128_t) + l;
v = newa(uint8_t, sz);
/* Fetch some persistent data unique to this machine */
r = sd_id128_get_machine((sd_id128_t*) v);
if (r < 0)
return r;
memcpy(v + sizeof(sd_id128_t), name, l);
/* Let's hash the machine ID plus the device name. We use
* a fixed, but originally randomly created hash key here. */
*ret = htole64(siphash24(v, sz, key->bytes));
return 0;
}
typedef struct Link {
const char *ifname;
} Link;
int net_verify_hardware_address(
const char *ifname,
bool is_static,
uint16_t iftype,
const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */
struct hw_addr_data *new_hw_addr) {
Link link = { .ifname = ifname };
assert(new_hw_addr);
if (new_hw_addr->length == 0)
return 0;
if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) {
if (is_static)
log_link_warning(&link,
"Specified MAC address with invalid length (%zu, expected %zu), refusing.",
new_hw_addr->length, arphrd_to_hw_addr_len(iftype));
return -EINVAL;
}
switch (iftype) {
case ARPHRD_ETHER:
/* see eth_random_addr() in the kernel */
if (ether_addr_is_null(&new_hw_addr->ether)) {
if (is_static)
log_link_warning(&link, "Specified MAC address is null, refusing.");
return -EINVAL;
}
if (ether_addr_is_broadcast(&new_hw_addr->ether)) {
if (is_static)
log_link_warning(&link, "Specified MAC address is broadcast, refusing.");
return -EINVAL;
}
if (ether_addr_is_multicast(&new_hw_addr->ether)) {
if (is_static)
log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit.");
new_hw_addr->bytes[0] &= 0xfe;
}
if (!is_static && !ether_addr_is_local(&new_hw_addr->ether))
/* Adjust local assignment bit when the MAC address is generated randomly. */
new_hw_addr->bytes[0] |= 0x02;
break;
case ARPHRD_INFINIBAND:
/* see ipoib_check_lladdr() in the kernel */
assert(ib_hw_addr);
assert(ib_hw_addr->length == INFINIBAND_ALEN);
if (is_static &&
(!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) ||
memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0))
log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes.");
if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) {
if (is_static)
log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing.");
return -EINVAL;
}
memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8);
break;
default:
if (is_static)
log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.",
strna(arphrd_to_name(iftype)), iftype);
return -EINVAL;
}
return 0;
}
#endif /* NM_IGNORED */

View file

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include "sd-device.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
bool netif_has_carrier(uint8_t operstate, unsigned flags);
int net_get_type_string(sd_device *device, uint16_t iftype, char **ret);
const char *net_get_persistent_name(sd_device *device);
int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret);
int
net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret);
int net_verify_hardware_address(const char *ifname,
bool is_static,
uint16_t iftype,
const struct hw_addr_data *ib_hw_addr,
struct hw_addr_data *new_hw_addr);

View file

@ -12,15 +12,6 @@
/*****************************************************************************/
int
asynchronous_close(int fd)
{
safe_close(fd);
return -1;
}
/*****************************************************************************/
sd_device *
sd_device_ref(sd_device *self)
{

View file

@ -86,7 +86,6 @@ sd_notify(int unset_environment, const char *state)
#include "sd-id128.h"
#include "sparse-endian.h"
#include "async.h"
#endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */

View file

@ -0,0 +1,83 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-device.h"
#include "sd-dhcp-duid.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
#define SYSTEMD_PEN 43793
typedef enum DUIDType {
DUID_TYPE_LLT = SD_DUID_TYPE_LLT,
DUID_TYPE_EN = SD_DUID_TYPE_EN,
DUID_TYPE_LL = SD_DUID_TYPE_LL,
DUID_TYPE_UUID = SD_DUID_TYPE_UUID,
_DUID_TYPE_MAX,
_DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 8415 section 11.1:
* A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
* octets that make up the actual identifier. The length of the DUID (not including the type code) is at
* least 1 octet and at most 128 octets. */
#define MIN_DUID_DATA_LEN 1
#define MAX_DUID_DATA_LEN 128
#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
#define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
struct duid {
be16_t type;
union {
struct {
/* DUID_TYPE_LLT */
be16_t htype;
be32_t time;
uint8_t haddr[];
} _packed_ llt;
struct {
/* DUID_TYPE_EN */
be32_t pen;
uint8_t id[];
} _packed_ en;
struct {
/* DUID_TYPE_LL */
be16_t htype;
uint8_t haddr[];
} _packed_ ll;
struct {
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
uint8_t data[MAX_DUID_DATA_LEN];
};
} _packed_;
typedef struct sd_dhcp_duid {
size_t size;
union {
struct duid duid;
uint8_t raw[MAX_DUID_LEN];
};
} sd_dhcp_duid;
static inline bool duid_size_is_valid(size_t size) {
return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
}
static inline bool duid_data_size_is_valid(size_t size) {
return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
}
const char *duid_type_to_string(DUIDType t) _const_;
int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret);

View file

@ -1,252 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-core.h"
#include <linux/if_infiniband.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include "dhcp-identifier.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "string-table.h"
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
[DUID_TYPE_LLT] = "DUID-LLT",
[DUID_TYPE_EN] = "DUID-EN/Vendor",
[DUID_TYPE_LL] = "DUID-LL",
[DUID_TYPE_UUID] = "UUID",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
struct duid d;
assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
if (duid_len > MAX_DUID_LEN)
return -EINVAL;
if (!strict)
/* Strict validation is not requested. We only ensure that the
* DUID is not too long. */
return 0;
switch (duid_type) {
case DUID_TYPE_LLT:
if (duid_len <= sizeof(d.llt))
return -EINVAL;
break;
case DUID_TYPE_EN:
if (duid_len != sizeof(d.en))
return -EINVAL;
break;
case DUID_TYPE_LL:
if (duid_len <= sizeof(d.ll))
return -EINVAL;
break;
case DUID_TYPE_UUID:
if (duid_len != sizeof(d.uuid))
return -EINVAL;
break;
default:
/* accept unknown type in order to be forward compatible */
break;
}
return 0;
}
#if 0 /* NM_IGNORED */
static int dhcp_identifier_set_duid_llt(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t t,
struct duid *ret_duid,
size_t *ret_len) {
uint16_t time_from_2000y;
assert(hw_addr);
assert(ret_duid);
assert(ret_len);
if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
if (t < USEC_2000)
time_from_2000y = 0;
else
time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
*ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
return 0;
}
static int dhcp_identifier_set_duid_ll(
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
struct duid *ret_duid,
size_t *ret_len) {
assert(hw_addr);
assert(ret_duid);
assert(ret_len);
if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
*ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
return 0;
}
#endif /* NM_IGNORED */
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
uint64_t hash;
int r;
assert(ret_duid);
assert(ret_len);
if (!test_mode) {
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
} else
/* For tests, especially for fuzzers, reproducibility is important.
* Hence, use a static and constant machine ID.
* See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
* directly; duid->en.id might not be aligned, so we need to copy */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id));
*ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id);
if (test_mode)
assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
return 0;
}
#if 0 /* NM_IGNORED */
static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
sd_id128_t machine_id;
int r;
assert(ret_duid);
assert(ret_len);
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
if (r < 0)
return r;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
*ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
return 0;
}
int dhcp_identifier_set_duid(
DUIDType duid_type,
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
struct duid *ret_duid,
size_t *ret_len) {
switch (duid_type) {
case DUID_TYPE_LLT:
return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
case DUID_TYPE_EN:
return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
case DUID_TYPE_LL:
return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
case DUID_TYPE_UUID:
return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
default:
return -EINVAL;
}
}
#endif /* NM_IGNORED */
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret) {
#if 0 /* NM_IGNORED */
const char *name = NULL;
uint32_t id32;
uint64_t id;
assert(hw_addr);
assert(ret);
if (dev)
name = net_get_persistent_name(dev);
if (name)
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
id32 = (id & 0xffffffff) ^ (id >> 32);
if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */
id32 = bswap_32(id32);
else
/* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy
* behavior. */
id32 = be32toh(id32);
unaligned_write_ne32(ret, id32);
return 0;
#else /* NM_IGNORED */
/* for NetworkManager, we don't use this function and we should never call here.
* This got replaced by nm_utils_create_dhcp_iaid(). */
g_return_val_if_reached (-EINVAL);
#endif /* NM_IGNORED */
}

View file

@ -1,75 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-device.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
#include "time-util.h"
#include "unaligned.h"
#define SYSTEMD_PEN 43793
typedef enum DUIDType {
DUID_TYPE_LLT = 1,
DUID_TYPE_EN = 2,
DUID_TYPE_LL = 3,
DUID_TYPE_UUID = 4,
_DUID_TYPE_MAX,
_DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 3315 section 9.1:
* A DUID can be no more than 128 octets long (not including the type code).
*/
#define MAX_DUID_LEN 128
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
struct duid {
be16_t type;
union {
struct {
/* DUID_TYPE_LLT */
be16_t htype;
be32_t time;
uint8_t haddr[0];
} _packed_ llt;
struct {
/* DUID_TYPE_EN */
be32_t pen;
uint8_t id[8];
} _packed_ en;
struct {
/* DUID_TYPE_LL */
be16_t htype;
uint8_t haddr[0];
} _packed_ ll;
struct {
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
struct {
uint8_t data[MAX_DUID_LEN];
} _packed_ raw;
};
} _packed_;
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
int dhcp_identifier_set_duid(
DUIDType duid_type,
const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
struct duid *ret_duid,
size_t *ret_len);
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret);
const char *duid_type_to_string(DUIDType t) _const_;

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-dhcp6-client.h"
int dhcp6_client_set_state_callback(
sd_dhcp6_client *client,
sd_dhcp6_client_callback_t cb,
void *userdata);
int dhcp6_client_get_state(sd_dhcp6_client *client);

View file

@ -11,7 +11,8 @@
#include "sd-event.h"
#include "sd-dhcp6-client.h"
#include "dhcp-identifier.h"
#include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "ether-addr-util.h"
@ -63,8 +64,7 @@ struct sd_dhcp6_client {
DHCP6IA ia_na;
DHCP6IA ia_pd;
DHCP6RequestIA request_ia;
struct duid duid;
size_t duid_len;
sd_dhcp_duid duid;
be16_t *req_opts;
size_t n_req_opts;
char *fqdn;
@ -79,10 +79,9 @@ struct sd_dhcp6_client {
sd_dhcp6_client_callback_t callback;
void *userdata;
sd_dhcp6_client_callback_t state_callback;
void *state_userdata;
bool send_release;
/* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
bool test_mode;
};
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
@ -90,7 +89,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
int dhcp6_client_send_message(sd_dhcp6_client *client);
void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode);
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
#define log_dhcp6_client_errno(client, error, fmt, ...) \

View file

@ -10,7 +10,9 @@
#include "sd-dhcp6-lease.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "macro.h"
#include "set.h"
#include "time-util.h"
struct sd_dhcp6_lease {
@ -43,9 +45,11 @@ struct sd_dhcp6_lease {
struct in6_addr *sntp;
size_t sntp_count;
char *fqdn;
char *captive_portal;
struct sd_dhcp6_option **sorted_vendor_options;
Set *vendor_options;
};
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid);
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
@ -60,6 +64,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
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_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret);
int dhcp6_lease_new_from_message(
@ -69,3 +74,17 @@ int dhcp6_lease_new_from_message(
const triple_timestamp *timestamp,
const struct in6_addr *server_address,
sd_dhcp6_lease **ret);
#define _FOREACH_DHCP6_ADDRESS(lease, it) \
for (int it = sd_dhcp6_lease_address_iterator_reset(lease); \
it > 0; \
it = sd_dhcp6_lease_address_iterator_next(lease))
#define FOREACH_DHCP6_ADDRESS(lease) \
_FOREACH_DHCP6_ADDRESS(lease, UNIQ_T(i, UNIQ))
#define _FOREACH_DHCP6_PD_PREFIX(lease, it) \
for (int it = sd_dhcp6_lease_pd_iterator_reset(lease); \
it > 0; \
it = sd_dhcp6_lease_pd_iterator_next(lease))
#define FOREACH_DHCP6_PD_PREFIX(lease) \
_FOREACH_DHCP6_PD_PREFIX(lease, UNIQ_T(i, UNIQ))

View file

@ -17,6 +17,7 @@
#include "dns-domain.h"
#include "escape.h"
#include "memory-util.h"
#include "network-common.h"
#include "strv.h"
#include "unaligned.h"
@ -526,6 +527,26 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
return status;
}
/* parse a string from dhcp option field. *ret must be initialized */
int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret) {
_cleanup_free_ char *string = NULL;
int r;
assert(data || data_len == 0);
assert(ret);
if (data_len <= 0) {
*ret = mfree(*ret);
return 0;
}
r = make_cstring((const char *) data, data_len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &string);
if (r < 0)
return r;
return free_and_replace(*ret, string);
}
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
int r;
@ -567,7 +588,7 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
uint32_t lt_valid, lt_pref;
usec_t lt_valid, lt_pref;
int r;
assert(ia);
@ -586,17 +607,18 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
lt_valid = be32toh(a->iaaddr.lifetime_valid);
lt_pref = be32toh(a->iaaddr.lifetime_preferred);
lt_valid = be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true);
lt_pref = be32_sec_to_usec(a->iaaddr.lifetime_preferred, /* max_as_infinity = */ true);
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);
"Received an IA address with preferred lifetime %s "
"larger than valid lifetime %s, ignoring.",
FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
if (len > sizeof(struct iaaddr)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
@ -610,7 +632,7 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
_cleanup_free_ DHCP6Address *a = NULL;
uint32_t lt_valid, lt_pref;
usec_t lt_valid, lt_pref;
int r;
assert(ia);
@ -629,17 +651,18 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
lt_valid = be32toh(a->iapdprefix.lifetime_valid);
lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
lt_valid = be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true);
lt_pref = be32_sec_to_usec(a->iapdprefix.lifetime_preferred, /* max_as_infinity = */ true);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"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);
"Received a PD prefix with preferred lifetime %s "
"larger than valid lifetime %s, ignoring.",
FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
if (len > sizeof(struct iapdprefix)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
@ -660,7 +683,7 @@ int dhcp6_option_parse_ia(
DHCP6IA **ret) {
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
uint32_t lt_t1, lt_t2;
usec_t lt_t1, lt_t2;
size_t header_len;
int r;
@ -710,17 +733,18 @@ int dhcp6_option_parse_ia(
"from the one chosen by the client, ignoring.");
/* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
lt_t1 = be32toh(ia->header.lifetime_t1);
lt_t2 = be32toh(ia->header.lifetime_t2);
lt_t1 = be32_sec_to_usec(ia->header.lifetime_t1, /* max_as_infinity = */ true);
lt_t2 = be32_sec_to_usec(ia->header.lifetime_t2, /* max_as_infinity = */ true);
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);
"Received an IA option with T1 %s > T2 %s, ignoring.",
FORMAT_TIMESPAN(lt_t1, USEC_PER_SEC),
FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
if (lt_t1 == 0 && lt_t2 > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.",
lt_t2);
"Received an IA option with zero T1 and non-zero T2 (%s), ignoring.",
FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
for (size_t offset = header_len; offset < option_data_len;) {
const uint8_t *subdata;

View file

@ -88,6 +88,7 @@ int dhcp6_option_parse(
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_string(const uint8_t *data, size_t data_len, char **ret);
int dhcp6_option_parse_ia(
sd_dhcp6_client *client,
be32_t iaid,

View file

@ -2,8 +2,11 @@
#include "nm-sd-adapt-core.h"
#include "env-util.h"
#include "format-util.h"
#include "network-common.h"
#include "socket-util.h"
#include "unaligned.h"
int get_ifname(int ifindex, char **ifname) {
assert(ifname);
@ -15,3 +18,111 @@ int get_ifname(int ifindex, char **ifname) {
return format_ifname_alloc(ifindex, ifname);
}
usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity) {
uint32_t s = unaligned_read_be32(ASSERT_PTR(p));
if (s == UINT32_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_SEC;
}
usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity) {
uint32_t s = be32toh(t);
if (s == UINT32_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_SEC;
}
usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity) {
uint32_t s = be32toh(t);
if (s == UINT32_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_MSEC;
}
usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity) {
uint16_t s = be16toh(t);
if (s == UINT16_MAX && max_as_infinity)
return USEC_INFINITY;
return s * USEC_PER_SEC;
}
be32_t usec_to_be32_sec(usec_t t) {
if (t == USEC_INFINITY)
/* Some settings, e.g. a lifetime of an address, UINT32_MAX is handled as infinity. so let's
* map USEC_INFINITY to UINT32_MAX. */
return htobe32(UINT32_MAX);
if (t >= (UINT32_MAX - 1) * USEC_PER_SEC)
/* Finite but too large. Let's use the largest (or off-by-one from the largest) finite value. */
return htobe32(UINT32_MAX - 1);
return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_SEC));
}
be32_t usec_to_be32_msec(usec_t t) {
if (t == USEC_INFINITY)
return htobe32(UINT32_MAX);
if (t >= (UINT32_MAX - 1) * USEC_PER_MSEC)
return htobe32(UINT32_MAX - 1);
return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_MSEC));
}
be16_t usec_to_be16_sec(usec_t t) {
if (t == USEC_INFINITY)
return htobe16(UINT16_MAX);
if (t >= (UINT16_MAX - 1) * USEC_PER_SEC)
return htobe16(UINT16_MAX - 1);
return htobe16((uint16_t) DIV_ROUND_UP(t, USEC_PER_SEC));
}
usec_t time_span_to_stamp(usec_t span, usec_t base) {
/* Typically, 0 lifetime (timespan) indicates the corresponding configuration (address or so) must be
* dropped. So, when the timespan is zero, here we return 0 rather than 'base'. This makes the caller
* easily understand that the configuration needs to be dropped immediately. */
if (span == 0)
return 0;
return usec_add(base, span);
}
bool network_test_mode_enabled(void) {
static int test_mode = -1;
int r;
if (test_mode < 0) {
r = getenv_bool("SYSTEMD_NETWORK_TEST_MODE");
if (r < 0) {
if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_NETWORK_TEST_MODE environment variable, ignoring: %m");
test_mode = false;
} else
test_mode = r;
}
return test_mode;
}
triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh) {
assert(t);
assert(mh);
struct timeval *tv = CMSG_FIND_AND_COPY_DATA(mh, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
if (tv)
return triple_timestamp_from_realtime(t, timeval_load(tv));
return triple_timestamp_now(t);
}

View file

@ -1,7 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/socket.h>
#include "log-link.h"
#include "sparse-endian.h"
#include "time-util.h"
#define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \
({ \
@ -28,3 +32,18 @@
})
int get_ifname(int ifindex, char **ifname);
usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity);
usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity);
usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity);
usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity);
be32_t usec_to_be32_sec(usec_t t);
be32_t usec_to_be32_msec(usec_t t);
be16_t usec_to_be16_sec(usec_t t);
usec_t time_span_to_stamp(usec_t span, usec_t base);
bool network_test_mode_enabled(void);
triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh);
#define TRIPLE_TIMESTAMP_FROM_CMSG(mh) \
triple_timestamp_from_cmsg(&(triple_timestamp) {}, mh)

View file

@ -0,0 +1,290 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-core.h"
#include <linux/if_infiniband.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include "dhcp-duid-internal.h"
#include "hexdecoct.h"
#include "netif-util.h"
#include "network-common.h"
#include "siphash24.h"
#include "string-table.h"
#include "unaligned.h"
#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
static const char * const duid_type_table[_DUID_TYPE_MAX] = {
[DUID_TYPE_LLT] = "DUID-LLT",
[DUID_TYPE_EN] = "DUID-EN/Vendor",
[DUID_TYPE_LL] = "DUID-LL",
[DUID_TYPE_UUID] = "UUID",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
assert_return(duid, -EINVAL);
*duid = (sd_dhcp_duid) {};
return 0;
}
int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
if (!duid)
return false;
return duid_size_is_valid(duid->size);
}
int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
assert_return(ret_type, -EINVAL);
assert_return(ret_data, -EINVAL);
assert_return(ret_size, -EINVAL);
*ret_type = be16toh(duid->duid.type);
*ret_data = duid->duid.data;
*ret_size = duid->size - offsetof(struct duid, data);
return 0;
}
int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
assert_return(ret_data, -EINVAL);
assert_return(ret_size, -EINVAL);
/* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
*ret_data = duid->raw;
*ret_size = duid->size;
return 0;
}
int sd_dhcp_duid_set(
sd_dhcp_duid *duid,
uint16_t duid_type,
const void *data,
size_t data_size) {
assert_return(duid, -EINVAL);
assert_return(data, -EINVAL);
if (!duid_data_size_is_valid(data_size))
return -EINVAL;
unaligned_write_be16(&duid->duid.type, duid_type);
memcpy(duid->duid.data, data, data_size);
duid->size = offsetof(struct duid, data) + data_size;
return 0;
}
int sd_dhcp_duid_set_raw(
sd_dhcp_duid *duid,
const void *data,
size_t data_size) {
assert_return(duid, -EINVAL);
assert_return(data, -EINVAL);
/* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
if (!duid_size_is_valid(data_size))
return -EINVAL;
memcpy(duid->raw, data, data_size);
duid->size = data_size;
return 0;
}
int sd_dhcp_duid_set_llt(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type,
uint64_t usec) {
uint16_t time_from_2000y;
assert_return(duid, -EINVAL);
assert_return(hw_addr, -EINVAL);
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
unaligned_write_be16(&duid->duid.llt.htype, arp_type);
unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
return 0;
}
int sd_dhcp_duid_set_ll(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type) {
assert_return(duid, -EINVAL);
assert_return(hw_addr, -EINVAL);
if (arp_type == ARPHRD_ETHER)
assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
unaligned_write_be16(&duid->duid.ll.htype, arp_type);
memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
return 0;
}
int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
sd_id128_t machine_id;
bool test_mode;
uint64_t hash;
int r;
assert_return(duid, -EINVAL);
test_mode = network_test_mode_enabled();
if (!test_mode) {
r = sd_id128_get_machine(&machine_id);
if (r < 0)
return r;
} else
/* For tests, especially for fuzzers, reproducibility is important.
* Hence, use a static and constant machine ID.
* See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
* directly; duid->en.id might not be aligned, so we need to copy */
hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
memcpy(duid->duid.en.id, &hash, sizeof(hash));
duid->size = offsetof(struct duid, en.id) + sizeof(hash);
if (test_mode)
assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
return 0;
}
int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
sd_id128_t machine_id;
int r;
assert_return(duid, -EINVAL);
r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
if (r < 0)
return r;
unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
return 0;
}
int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
_cleanup_free_ char *p = NULL, *x = NULL;
const char *t;
assert(data);
assert(ret);
if (!duid_data_size_is_valid(data_size))
return -EINVAL;
x = hexmem(data, data_size);
if (!x)
return -ENOMEM;
t = duid_type_to_string(type);
if (!t)
return asprintf(ret, "%04x:%s", htobe16(type), x);
p = strjoin(t, ":", x);
if (!p)
return -ENOMEM;
*ret = TAKE_PTR(p);
return 0;
}
int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
uint16_t type;
const void *data;
size_t data_size;
int r;
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
assert_return(ret, -EINVAL);
r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
if (r < 0)
return r;
return dhcp_duid_to_string_internal(type, data, data_size, ret);
}
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret) {
const char *name = NULL;
uint32_t id32;
uint64_t id;
assert(hw_addr);
assert(ret);
if (dev)
name = net_get_persistent_name(dev);
if (name)
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
id32 = (id & 0xffffffff) ^ (id >> 32);
if (legacy_unstable_byteorder)
/* for historical reasons (a bug), the bits were swapped and thus
* the result was endianness dependent. Preserve that behavior. */
id32 = bswap_32(id32);
else
/* the fixed behavior returns a stable byte order. Since LE is expected
* to be more common, swap the bytes on LE to give the same as legacy
* behavior. */
id32 = be32toh(id32);
unaligned_write_ne32(ret, id32);
return 0;
}

View file

@ -7,27 +7,22 @@
#include <errno.h>
#include <sys/ioctl.h>
#if 0 /* NM_IGNORED */
#include <linux/if_arp.h>
#else /* NM_IGNORED */
#include <net/if_arp.h>
#endif /* NM_IGNORED */
#include <linux/if_infiniband.h>
#include "sd-dhcp6-client.h"
#include "alloc-util.h"
#include "device-util.h"
#include "dhcp-identifier.h"
#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "io-util.h"
#include "iovec-util.h"
#include "random-util.h"
#include "socket-util.h"
#include "sort-util.h"
@ -52,6 +47,19 @@ int sd_dhcp6_client_set_callback(
return 0;
}
int dhcp6_client_set_state_callback(
sd_dhcp6_client *client,
sd_dhcp6_client_callback_t cb,
void *userdata) {
assert_return(client, -EINVAL);
client->state_callback = cb;
client->state_userdata = userdata;
return 0;
}
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@ -184,10 +192,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
static int client_ensure_duid(sd_dhcp6_client *client) {
assert(client);
if (client->duid_len != 0)
if (sd_dhcp_duid_is_set(&client->duid))
return 0;
return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len);
return sd_dhcp6_client_set_duid_en(client);
}
/**
@ -195,101 +203,102 @@ static int client_ensure_duid(sd_dhcp6_client *client) {
* without further modification. Otherwise, if duid_type is supported, DUID
* is set based on that type. Otherwise, an error is returned.
*/
static int dhcp6_client_set_duid_internal(
sd_dhcp6_client *client,
DUIDType duid_type,
const void *duid,
size_t duid_len,
usec_t llt_time) {
int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(duid_len == 0 || duid, -EINVAL);
if (duid) {
r = dhcp_validate_duid_len(duid_type, duid_len, true);
if (r < 0) {
r = dhcp_validate_duid_len(duid_type, duid_len, false);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type);
}
client->duid.type = htobe16(duid_type);
memcpy(&client->duid.raw.data, duid, duid_len);
client->duid_len = sizeof(client->duid.type) + duid_len;
} else {
#if 0 /* NM_IGNORED */
r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
client->test_mode, &client->duid, &client->duid_len);
if (r == -EOPNOTSUPP)
return log_dhcp6_client_errno(client, r,
"Failed to set %s. MAC address is not set or "
"interface type is not supported.",
duid_type_to_string(duid_type));
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set %s: %m",
duid_type_to_string(duid_type));
#else /* NM_IGNORED */
g_return_val_if_reached (-EINVAL);
#endif /* NM_IGNORED */
}
r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
return 0;
}
int sd_dhcp6_client_set_duid(
sd_dhcp6_client *client,
uint16_t duid_type,
const void *duid,
size_t duid_len) {
return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
}
int sd_dhcp6_client_set_duid_llt(
sd_dhcp6_client *client,
usec_t llt_time) {
return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
}
int sd_dhcp6_client_duid_as_string(
sd_dhcp6_client *client,
char **duid) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
const char *v;
int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(client->duid_len > 0, -ENODATA);
assert_return(duid, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
v = duid_type_to_string(be16toh(client->duid.type));
if (v) {
s = strdup(v);
if (!s)
return -ENOMEM;
} else {
r = asprintf(&s, "%0x", client->duid.type);
if (r < 0)
return -ENOMEM;
}
t = hexmem(&client->duid.raw.data, client->duid_len);
if (!t)
return -ENOMEM;
p = strjoin(s, ":", t);
if (!p)
return -ENOMEM;
*duid = TAKE_PTR(p);
r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
return 0;
}
int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
r = sd_dhcp_duid_set_en(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
return 0;
}
int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
r = sd_dhcp_duid_set_uuid(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
return 0;
}
int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) {
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(duid || duid_len == 0, -EINVAL);
r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
return 0;
}
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
client->duid = *duid;
return 0;
}
int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
assert_return(client, -EINVAL);
assert_return(ret, -EINVAL);
if (!sd_dhcp_duid_is_set(&client->duid))
return -ENODATA;
*ret = &client->duid;
return 0;
}
int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
assert_return(client, -EINVAL);
assert_return(ret, -EINVAL);
if (!sd_dhcp_duid_is_set(&client->duid))
return -ENODATA;
return sd_dhcp_duid_to_string(&client->duid, ret);
}
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@ -335,12 +344,6 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
return 0;
}
void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) {
assert(client);
client->test_mode = test_mode;
}
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn) {
@ -491,7 +494,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
assert(client);
assert(client->test_mode);
assert_se(network_test_mode_enabled());
/* This is for tests or fuzzers. */
@ -510,7 +513,6 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
client->send_release = enable;
return 0;
@ -552,6 +554,15 @@ static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
client->state = state;
if (client->state_callback)
client->state_callback(client, state, client->state_userdata);
}
int dhcp6_client_get_state(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
return client->state;
}
static void client_notify(sd_dhcp6_client *client, int event) {
@ -809,9 +820,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
assert(client->duid_len > 0);
assert(sd_dhcp_duid_is_set(&client->duid));
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
client->duid_len, &client->duid);
client->duid.size, &client->duid.duid);
if (r < 0)
return r;
@ -824,7 +835,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
/* RFC 8415 Section 21.9.
* A client MUST include an Elapsed Time option in messages to indicate how long the client has
* been trying to complete a DHCP message exchange. */
elapsed_usec = NM_MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
elapsed_time = htobe16(elapsed_usec);
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
if (r < 0)
@ -1047,12 +1058,20 @@ static int client_enter_bound_state(sd_dhcp6_client *client) {
(void) event_source_disable(client->receive_message);
(void) event_source_disable(client->timeout_resend);
r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid);
r = sd_dhcp6_lease_get_t1(client->lease, &lifetime_t1);
if (r < 0)
goto error;
r = sd_dhcp6_lease_get_t2(client->lease, &lifetime_t2);
if (r < 0)
goto error;
r = sd_dhcp6_lease_get_valid_lifetime(client->lease, &lifetime_valid);
if (r < 0)
goto error;
lifetime_t2 = client_timeout_compute_random(lifetime_t2);
lifetime_t1 = client_timeout_compute_random(NM_MIN(lifetime_t1, lifetime_t2));
lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2));
if (lifetime_t1 == USEC_INFINITY) {
log_dhcp6_client(client, "Infinite T1");
@ -1286,16 +1305,15 @@ static int client_receive_message(
.msg_control = &control,
.msg_controllen = sizeof(control),
};
triple_timestamp t = {};
triple_timestamp t;
_cleanup_free_ DHCP6Message *message = NULL;
struct in6_addr *server_address = NULL;
ssize_t buflen, len;
buflen = next_datagram_size_fd(fd);
if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
return 0;
if (buflen < 0) {
if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
return 0;
log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
return 0;
}
@ -1307,10 +1325,9 @@ static int client_receive_message(
iov = IOVEC_MAKE(message, buflen);
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len))
return 0;
if (len < 0) {
if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
return 0;
log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
return 0;
}
@ -1329,9 +1346,7 @@ static int client_receive_message(
server_address = &sa.in6.sin6_addr;
}
struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval);
if (tv)
triple_timestamp_from_realtime(&t, timeval_load(tv));
triple_timestamp_from_cmsg(&t, &msg);
if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
return 0;

View file

@ -10,7 +10,9 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "network-common.h"
#include "strv.h"
#include "unaligned.h"
#define IRT_DEFAULT (1 * USEC_PER_DAY)
#define IRT_MINIMUM (600 * USEC_PER_SEC)
@ -21,7 +23,7 @@ static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timest
if (timestamp && triple_timestamp_is_set(timestamp))
lease->timestamp = *timestamp;
else
triple_timestamp_get(&lease->timestamp);
triple_timestamp_now(&lease->timestamp);
}
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
@ -37,30 +39,26 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
return 0;
}
static usec_t sec2usec(uint32_t sec) {
return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC;
}
static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
assert(lease);
assert(lease->ia_na || lease->ia_pd);
if (lease->ia_na) {
t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1));
t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2));
t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true));
t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true));
LIST_FOREACH(addresses, a, lease->ia_na->addresses)
min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true));
}
if (lease->ia_pd) {
t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true));
t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true));
LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
}
if (t2 == 0 || t2 > min_valid_lt) {
@ -70,25 +68,52 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
t2 = min_valid_lt / 10 * 8;
}
lease->lifetime_valid = sec2usec(min_valid_lt);
lease->lifetime_t1 = sec2usec(t1);
lease->lifetime_t2 = sec2usec(t2);
lease->lifetime_valid = min_valid_lt;
lease->lifetime_t1 = t1;
lease->lifetime_t2 = t2;
}
int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) {
assert(lease);
#define DEFINE_GET_TIME_FUNCTIONS(name, val) \
int sd_dhcp6_lease_get_##name( \
sd_dhcp6_lease *lease, \
uint64_t *ret) { \
\
assert_return(lease, -EINVAL); \
\
if (!lease->ia_na && !lease->ia_pd) \
return -ENODATA; \
\
if (ret) \
*ret = lease->val; \
return 0; \
} \
\
int sd_dhcp6_lease_get_##name##_timestamp( \
sd_dhcp6_lease *lease, \
clockid_t clock, \
uint64_t *ret) { \
\
usec_t s, t; \
int r; \
\
assert_return(lease, -EINVAL); \
\
r = sd_dhcp6_lease_get_##name(lease, &s); \
if (r < 0) \
return r; \
\
r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
if (r < 0) \
return r; \
\
if (ret) \
*ret = time_span_to_stamp(s, t); \
return 0; \
}
if (!lease->ia_na && !lease->ia_pd)
return -ENODATA;
if (ret_t1)
*ret_t1 = lease->lifetime_t1;
if (ret_t2)
*ret_t2 = lease->lifetime_t2;
if (ret_valid)
*ret_valid = lease->lifetime_valid;
return 0;
}
DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1);
DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t1);
DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid);
static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
assert(lease);
@ -218,61 +243,151 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
return 0;
}
int sd_dhcp6_lease_get_address(
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
assert_return(lease, -EINVAL);
if (!lease->addr_iter)
return -ENODATA;
if (ret)
*ret = lease->addr_iter->iaaddr.address;
return 0;
}
int sd_dhcp6_lease_get_address_lifetime(
sd_dhcp6_lease *lease,
struct in6_addr *ret_addr,
uint32_t *ret_lifetime_preferred,
uint32_t *ret_lifetime_valid) {
usec_t *ret_lifetime_preferred,
usec_t *ret_lifetime_valid) {
const struct iaaddr *a;
assert_return(lease, -EINVAL);
if (!lease->addr_iter)
return -ENODATA;
if (ret_addr)
*ret_addr = lease->addr_iter->iaaddr.address;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
a = &lease->addr_iter->iaaddr;
lease->addr_iter = lease->addr_iter->addresses_next;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
return 0;
}
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) {
if (!lease)
return false;
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
return !!lease->addr_iter;
}
int sd_dhcp6_lease_get_pd(
int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) {
if (!lease || !lease->addr_iter)
return false;
lease->addr_iter = lease->addr_iter->addresses_next;
return !!lease->addr_iter;
}
int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) {
return lease && lease->ia_na;
}
int sd_dhcp6_lease_get_pd_prefix(
sd_dhcp6_lease *lease,
struct in6_addr *ret_prefix,
uint8_t *ret_prefix_len,
uint32_t *ret_lifetime_preferred,
uint32_t *ret_lifetime_valid) {
uint8_t *ret_prefix_len) {
const struct iapdprefix *a;
assert_return(lease, -EINVAL);
if (!lease->prefix_iter)
return -ENODATA;
if (ret_prefix)
*ret_prefix = lease->prefix_iter->iapdprefix.address;
if (ret_prefix_len)
*ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
a = &lease->prefix_iter->iapdprefix;
lease->prefix_iter = lease->prefix_iter->addresses_next;
if (ret_prefix)
*ret_prefix = a->address;
if (ret_prefix_len)
*ret_prefix_len = a->prefixlen;
return 0;
}
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
if (lease)
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
int sd_dhcp6_lease_get_pd_lifetime(
sd_dhcp6_lease *lease,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid) {
const struct iapdprefix *a;
assert_return(lease, -EINVAL);
if (!lease->prefix_iter)
return -ENODATA;
a = &lease->prefix_iter->iapdprefix;
if (ret_lifetime_preferred)
*ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true);
if (ret_lifetime_valid)
*ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true);
return 0;
}
int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) {
if (!lease)
return false;
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
return !!lease->prefix_iter;
}
int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) {
if (!lease || !lease->prefix_iter)
return false;
lease->prefix_iter = lease->prefix_iter->addresses_next;
return !!lease->prefix_iter;
}
#define DEFINE_GET_TIMESTAMP2(name) \
int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \
sd_dhcp6_lease *lease, \
clockid_t clock, \
uint64_t *ret_lifetime_preferred, \
uint64_t *ret_lifetime_valid) { \
\
usec_t t, p, v; \
int r; \
\
assert_return(lease, -EINVAL); \
\
r = sd_dhcp6_lease_get_##name##_lifetime( \
lease, \
ret_lifetime_preferred ? &p : NULL, \
ret_lifetime_valid ? &v : NULL); \
if (r < 0) \
return r; \
\
r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \
if (r < 0) \
return r; \
\
if (ret_lifetime_preferred) \
*ret_lifetime_preferred = time_span_to_stamp(p, t); \
if (ret_lifetime_valid) \
*ret_lifetime_valid = time_span_to_stamp(v, t); \
return 0; \
}
DEFINE_GET_TIMESTAMP2(address);
DEFINE_GET_TIMESTAMP2(pd);
int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
return lease && lease->ia_pd;
}
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
@ -447,6 +562,111 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
return 0;
}
int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
_cleanup_free_ char *uri = NULL;
int r;
assert(lease);
assert(optval || optlen == 0);
r = dhcp6_option_parse_string(optval, optlen, &uri);
if (r < 0)
return r;
if (uri && !in_charset(uri, URI_VALID))
return -EINVAL;
return free_and_replace(lease->captive_portal, uri);
}
int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) {
assert_return(lease, -EINVAL);
assert_return(ret, -EINVAL);
if (!lease->captive_portal)
return -ENODATA;
*ret = lease->captive_portal;
return 0;
}
int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) {
int r;
assert_return(lease, -EINVAL);
if (set_isempty(lease->vendor_options))
return -ENODATA;
if (ret) {
if (!lease->sorted_vendor_options) {
r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL);
if (r < 0)
return r;
}
*ret = lease->sorted_vendor_options;
}
return set_size(lease->vendor_options);
}
static int dhcp6_lease_insert_vendor_option(
sd_dhcp6_lease *lease,
uint16_t option_code,
const void *data,
size_t len,
uint32_t enterprise_id) {
_cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL;
assert(lease);
option = new(sd_dhcp6_option, 1);
if (!option)
return -ENOMEM;
*option = (sd_dhcp6_option) {
.n_ref = 1,
.enterprise_identifier = enterprise_id,
.option = option_code,
.length = len,
};
option->data = memdup_suffix0(data, len);
if (!option->data)
return -ENOMEM;
return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option));
}
static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
uint32_t enterprise_id;
assert(lease);
assert(optval || optlen == 0);
if (optlen < sizeof(be32_t))
return -EBADMSG;
enterprise_id = unaligned_read_be32(optval);
for (size_t offset = 4; offset < optlen;) {
const uint8_t *subval;
size_t sublen;
uint16_t subopt;
r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
if (r < 0)
return r;
r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id);
if (r < 0)
return r;
}
return 0;
}
static int dhcp6_lease_parse_message(
sd_dhcp6_client *client,
sd_dhcp6_lease *lease,
@ -467,6 +687,11 @@ static int dhcp6_lease_parse_message(
size_t optlen;
const uint8_t *optval;
if (len - offset < offsetof(DHCP6Option, data)) {
log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset);
break;
}
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
if (r < 0)
return log_dhcp6_client_errno(client, r,
@ -607,6 +832,12 @@ static int dhcp6_lease_parse_message(
break;
case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
r = dhcp6_lease_set_captive_portal(lease, optval, optlen);
if (r < 0)
log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m");
break;
case SD_DHCP6_OPTION_CLIENT_FQDN:
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
if (r < 0)
@ -619,7 +850,14 @@ static int dhcp6_lease_parse_message(
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received information refresh time option with an invalid length (%zu).", optlen);
irt = unaligned_read_be32(optval) * USEC_PER_SEC;
irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
break;
case SD_DHCP6_OPTION_VENDOR_OPTS:
r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
if (r < 0)
log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m");
break;
}
}
@ -631,7 +869,7 @@ static int dhcp6_lease_parse_message(
"%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
@ -661,12 +899,15 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
if (!lease)
return NULL;
set_free(lease->vendor_options);
free(lease->sorted_vendor_options);
free(lease->clientid);
free(lease->serverid);
dhcp6_ia_free(lease->ia_na);
dhcp6_ia_free(lease->ia_pd);
free(lease->dns);
free(lease->fqdn);
free(lease->captive_portal);
strv_free(lease->domains);
free(lease->ntp);
strv_free(lease->ntp_fqdn);

View file

@ -7,39 +7,41 @@
#include "sd-device.h"
#include "alloc-util.h"
#include "log.h"
#include "macro.h"
#include "strv.h"
#define device_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (key = sd_device_get_property_first(device, &(value)); \
key; \
key = sd_device_get_property_next(device, &(value)))
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (const char *value, *key = sd_device_get_property_first(device, &value); \
key; \
key = sd_device_get_property_next(device, &value))
#define FOREACH_DEVICE_TAG(device, tag) \
for (tag = sd_device_get_tag_first(device); \
tag; \
#define FOREACH_DEVICE_TAG(device, tag) \
for (const char *tag = sd_device_get_tag_first(device); \
tag; \
tag = sd_device_get_tag_next(device))
#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
for (tag = sd_device_get_current_tag_first(device); \
tag; \
#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \
for (const char *tag = sd_device_get_current_tag_first(device); \
tag; \
tag = sd_device_get_current_tag_next(device))
#define FOREACH_DEVICE_SYSATTR(device, attr) \
for (attr = sd_device_get_sysattr_first(device); \
attr; \
#define FOREACH_DEVICE_SYSATTR(device, attr) \
for (const char *attr = sd_device_get_sysattr_first(device); \
attr; \
attr = sd_device_get_sysattr_next(device))
#define FOREACH_DEVICE_DEVLINK(device, devlink) \
for (devlink = sd_device_get_devlink_first(device); \
devlink; \
#define FOREACH_DEVICE_DEVLINK(device, devlink) \
for (const char *devlink = sd_device_get_devlink_first(device); \
devlink; \
devlink = sd_device_get_devlink_next(device))
#define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \
for (child = sd_device_get_child_first(device, suffix_ptr); \
for (sd_device *child = sd_device_get_child_first(device, suffix_ptr); \
child; \
child = sd_device_get_child_next(device, suffix_ptr))
@ -49,14 +51,14 @@
#define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \
_FOREACH_DEVICE_CHILD(device, child, &suffix)
#define FOREACH_DEVICE(enumerator, device) \
for (device = sd_device_enumerator_get_device_first(enumerator); \
device; \
#define FOREACH_DEVICE(enumerator, device) \
for (sd_device *device = sd_device_enumerator_get_device_first(enumerator); \
device; \
device = sd_device_enumerator_get_device_next(enumerator))
#define FOREACH_SUBSYSTEM(enumerator, device) \
for (device = sd_device_enumerator_get_subsystem_first(enumerator); \
device; \
#define FOREACH_SUBSYSTEM(enumerator, device) \
for (sd_device *device = sd_device_enumerator_get_subsystem_first(enumerator); \
device; \
device = sd_device_enumerator_get_subsystem_next(enumerator))
#define log_device_full_errno_zerook(device, level, error, ...) \
@ -81,17 +83,17 @@
#define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__)
#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__)
#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__)
#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__)
#define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__)
#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__)
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
int devname_from_devnum(mode_t mode, dev_t devnum, char **ret);
static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
@ -101,3 +103,13 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
char** device_make_log_fields(sd_device *device);
bool device_in_subsystem(sd_device *device, const char *subsystem);
bool device_is_devtype(sd_device *device, const char *devtype);
static inline bool device_property_can_set(const char *property) {
return property &&
!STR_IN_SET(property,
"ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
"IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
}

View file

@ -99,16 +99,21 @@ int event_reset_time_relative(
const char *description,
bool force_reset) {
usec_t usec_now;
int r;
assert(e);
r = sd_event_now(e, clock, &usec_now);
if (r < 0)
return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
if (usec > 0) {
usec_t usec_now;
return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
r = sd_event_now(e, clock, &usec_now);
if (r < 0)
return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
usec = usec_add(usec_now, usec);
}
return event_reset_time(e, s, clock, usec, accuracy, callback, userdata, priority, description, force_reset);
}
#if 0 /* NM_IGNORED */
@ -149,4 +154,21 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
return 0;
}
int event_add_child_pidref(
sd_event *e,
sd_event_source **s,
const PidRef *pid,
int options,
sd_event_child_handler_t callback,
void *userdata) {
if (!pidref_is_set(pid))
return -ESRCH;
if (pid->fd >= 0)
return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
}
#endif /* NM_IGNORED */

View file

@ -5,6 +5,8 @@
#include "sd-event.h"
#include "pidref.h"
int event_reset_time(
sd_event *e,
sd_event_source **s,
@ -32,3 +34,7 @@ static inline int event_source_disable(sd_event_source *s) {
}
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
#if 0 /* NM_IGNORED */
int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
#endif /* NM_IGNORED */

View file

@ -1169,10 +1169,10 @@ static int source_set_pending(sd_event_source *s, bool b) {
assert(s->inotify.inode_data->inotify_data);
if (b)
s->inotify.inode_data->inotify_data->n_pending ++;
s->inotify.inode_data->inotify_data->n_pending++;
else {
assert(s->inotify.inode_data->inotify_data->n_pending > 0);
s->inotify.inode_data->inotify_data->n_pending --;
s->inotify.inode_data->inotify_data->n_pending--;
}
}
@ -1983,7 +1983,7 @@ _public_ int sd_event_add_memory_pressure(
env = secure_getenv("MEMORY_PRESSURE_WRITE");
if (env) {
r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size);
r = unbase64mem(env, &write_buffer, &write_buffer_size);
if (r < 0)
return r;
}
@ -2239,8 +2239,8 @@ static int inode_data_compare(const struct inode_data *x, const struct inode_dat
static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
assert(d);
siphash24_compress(&d->dev, sizeof(d->dev), state);
siphash24_compress(&d->ino, sizeof(d->ino), state);
siphash24_compress_typesafe(d->dev, state);
siphash24_compress_typesafe(d->ino, state);
}
DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
@ -4008,7 +4008,7 @@ static int process_inotify(sd_event *e) {
if (r < 0)
return r;
if (r > 0)
done ++;
done++;
}
return done;
@ -4620,7 +4620,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
/* Set timestamp only when this is called first time. */
if (threshold == INT64_MAX)
triple_timestamp_get(&e->timestamp);
triple_timestamp_now(&e->timestamp);
for (size_t i = 0; i < m; i++) {
@ -5047,7 +5047,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
}
}
e->watchdog = !!b;
e->watchdog = b;
return e->watchdog;
fail:

View file

@ -11,9 +11,29 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
#include "sha256.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "sync-util.h"
#include "virt.h"
int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
sd_id128_t t;
int r;
assert(ret);
r = sd_id128_from_string(ASSERT_PTR(s), &t);
if (r < 0)
return r;
if (sd_id128_is_null(t))
return -ENXIO;
*ret = t;
return 0;
}
#if 0 /* NM_IGNORED */
bool id128_is_valid(const char *s) {
@ -24,7 +44,7 @@ bool id128_is_valid(const char *s) {
l = strlen(s);
if (l == SD_ID128_STRING_MAX - 1)
/* Plain formatted 128bit hex string */
/* Plain formatted 128-bit hex string */
return in_charset(s, HEXDIGITS);
if (l == SD_ID128_UUID_STRING_MAX - 1) {
@ -53,7 +73,7 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) {
assert(fd >= 0);
/* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
/* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both
* optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they
* aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you
* accept".
@ -151,7 +171,7 @@ int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) {
}
buffer[sz - 1] = '\n';
r = loop_write(fd, buffer, sz, false);
r = loop_write(fd, buffer, sz);
if (r < 0)
return r;
@ -178,11 +198,11 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(sd_id128_t), state);
siphash24_compress_typesafe(*p, state);
}
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
return memcmp(a, b, 16);
return memcmp(a, b, sizeof(sd_id128_t));
}
sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
@ -210,9 +230,22 @@ int id128_get_product(sd_id128_t *ret) {
/* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is
* particularly relevant in VM environments, where VM managers typically place a VM uuid there. */
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
if (r == -ENOENT)
r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
r = detect_container();
if (r < 0)
return r;
if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but
* of the host */
return -ENOENT;
FOREACH_STRING(i,
"/sys/class/dmi/id/product_uuid", /* KVM */
"/proc/device-tree/vm,uuid", /* Device tree */
"/sys/hypervisor/uuid") { /* Xen */
r = id128_read(i, ID128_FORMAT_UUID, &uuid);
if (r != -ENOENT)
break;
}
if (r < 0)
return r;
@ -222,4 +255,22 @@ int id128_get_product(sd_id128_t *ret) {
*ret = uuid;
return 0;
}
sd_id128_t id128_digest(const void *data, size_t size) {
assert(data || size == 0);
/* Hashes a UUID from some arbitrary data */
if (size == SIZE_MAX)
size = strlen(data);
uint8_t h[SHA256_DIGEST_SIZE];
sd_id128_t id;
/* Take the first half of the SHA256 result */
assert_cc(sizeof(h) >= sizeof(id.bytes));
memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes));
return id128_make_v4_uuid(id);
}
#endif /* NM_IGNORED */

View file

@ -6,6 +6,7 @@
#include "sd-id128.h"
#include "errno-util.h"
#include "hash-funcs.h"
#include "macro.h"
@ -20,6 +21,8 @@ typedef enum Id128Flag {
ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */
} Id128Flag;
int id128_from_string_nonzero(const char *s, sd_id128_t *ret);
int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret);
int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret);
static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) {
@ -44,9 +47,12 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id);
int id128_get_product(sd_id128_t *ret);
sd_id128_t id128_digest(const void *data, size_t size);
/* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_MACHINE_ID_UNSET(r) \
IN_SET(abs(r), \
ENOENT, \
ENOMEDIUM, \
ENOPKG)
#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
IN_SET(r, \
-ENOENT, \
-ENOMEDIUM, \
-ENOPKG)
_DEFINE_ABS_WRAPPER(MACHINE_ID_UNSET);

View file

@ -345,18 +345,20 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
return 0;
}
static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
uint8_t hmac[SHA256_DIGEST_SIZE];
sd_id128_t result;
_public_ int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
assert_cc(sizeof(sd_id128_t) < SHA256_DIGEST_SIZE); /* Check that we don't need to pad with zeros. */
union {
uint8_t hmac[SHA256_DIGEST_SIZE];
sd_id128_t result;
} buf;
assert(ret);
assert_return(ret, -EINVAL);
assert_return(!sd_id128_is_null(app_id), -ENXIO);
hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), buf.hmac);
/* Take only the first half. */
memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
*ret = id128_make_v4_uuid(result);
*ret = id128_make_v4_uuid(buf.result);
return 0;
}
@ -370,7 +372,7 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re
if (r < 0)
return r;
return get_app_specific(id, app_id, ret);
return sd_id128_get_app_specific(id, app_id, ret);
}
_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
@ -383,6 +385,6 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
if (r < 0)
return r;
return get_app_specific(id, app_id, ret);
return sd_id128_get_app_specific(id, app_id, ret);
}
#endif /* NM_IGNORED */

View file

@ -99,7 +99,7 @@ typedef void (*_sd_destroy_t)(void *userdata);
} \
struct _sd_useless_struct_to_allow_trailing_semicolon_
/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can
/* The following macro should be used in all public enums, to force 64-bit wideness on them, so that we can
* freely extend them later on, without breaking compatibility. */
#define _SD_ENUM_FORCE_S64(id) \
_SD_##id##_INT64_MIN = INT64_MIN, \

View file

@ -129,6 +129,7 @@ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumera
int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match);
int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match);
int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value);
int sd_device_enumerator_add_match_property_required(sd_device_enumerator *enumerator, const char *property, const char *value);
int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname);
int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);

View file

@ -0,0 +1,71 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosddhcpduidhfoo
#define foosddhcpduidhfoo
/***
Copyright © 2013 Intel Corporation. All rights reserved.
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <sys/types.h>
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
enum {
SD_DUID_TYPE_LLT = 1,
SD_DUID_TYPE_EN = 2,
SD_DUID_TYPE_LL = 3,
SD_DUID_TYPE_UUID = 4
};
typedef struct sd_dhcp_duid sd_dhcp_duid;
int sd_dhcp_duid_clear(sd_dhcp_duid *duid);
int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid);
int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size);
int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size);
int sd_dhcp_duid_set(
sd_dhcp_duid *duid,
uint16_t duid_type,
const void *data,
size_t data_size);
int sd_dhcp_duid_set_raw(
sd_dhcp_duid *duid,
const void *data,
size_t data_size);
int sd_dhcp_duid_set_llt(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type,
uint64_t usec);
int sd_dhcp_duid_set_ll(
sd_dhcp_duid *duid,
const void *hw_addr,
size_t hw_addr_size,
uint16_t arp_type);
int sd_dhcp_duid_set_en(sd_dhcp_duid *duid);
int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid);
int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret);
_SD_END_DECLARATIONS;
#endif

View file

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

View file

@ -23,6 +23,8 @@
#include <netinet/in.h>
#include <sys/types.h>
#include "sd-dhcp6-option.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
@ -30,24 +32,54 @@ _SD_BEGIN_DECLARATIONS;
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
int sd_dhcp6_lease_get_t1_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_t2(sd_dhcp6_lease *lease, uint64_t *ret);
int sd_dhcp6_lease_get_t2_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_valid_lifetime(sd_dhcp6_lease *lease, uint64_t *ret);
int sd_dhcp6_lease_get_valid_lifetime_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
struct in6_addr *addr,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
uint8_t *prefix_len,
uint32_t *lifetime_preferred,
uint32_t *lifetime_valid);
int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_address(
sd_dhcp6_lease *lease,
struct in6_addr *ret);
int sd_dhcp6_lease_get_address_lifetime(
sd_dhcp6_lease *lease,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_get_address_lifetime_timestamp(
sd_dhcp6_lease *lease,
clockid_t clock,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease);
int sd_dhcp6_lease_get_pd_prefix(
sd_dhcp6_lease *lease,
struct in6_addr *ret_prefix,
uint8_t *ret_prefix_length);
int sd_dhcp6_lease_get_pd_lifetime(
sd_dhcp6_lease *lease,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_get_pd_lifetime_timestamp(
sd_dhcp6_lease *lease,
clockid_t clock,
uint64_t *ret_lifetime_preferred,
uint64_t *ret_lifetime_valid);
int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
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);
int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret);
int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);

View file

@ -20,6 +20,8 @@
#include <inttypes.h>
#include <sys/types.h>
#include "sd-dhcp6-protocol.h"
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;

View file

@ -0,0 +1,174 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosddhcp6protocolhfoo
#define foosddhcp6protocolhfoo
/***
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
enum {
SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */
SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */
SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */
SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */
SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */
SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */
SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */
SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */
SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */
/* option code 10 is unassigned */
SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */
SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */
SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */
SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */
SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */
SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */
SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */
SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */
SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */
SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */
SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */
SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */
SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */
SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */
SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */
SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */
SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */
SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */
SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */
SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */
/* option code 35 is unassigned */
SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */
SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */
SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */
SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */
SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */
SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */
SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */
SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */
SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */
SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */
SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */
SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */
SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */
SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */
SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */
SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */
SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */
SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */
SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */
SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */
SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */
SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */
SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */
SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */
SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */
SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */
SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */
SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */
SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */
SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */
SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */
SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */
SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */
SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */
SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */
SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */
SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */
SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */
SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */
SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */
SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */
SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */
SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */
SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */
SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */
SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */
SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */
SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */
SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */
SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */
SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */
SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */
SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */
SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */
SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */
SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */
SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */
SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */
SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */
SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */
SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */
SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */
SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */
SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */
SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */
SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */
SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */
SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */
SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */
SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */
SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */
SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */
SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */
SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */
SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */
SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */
SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */
SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */
SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */
SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */
SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */
SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */
/* option codes 144-65535 are unassigned */
};
_SD_END_DECLARATIONS;
#endif

View file

@ -50,6 +50,7 @@ int sd_id128_get_machine(sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);

View file

@ -42,7 +42,8 @@ enum {
SD_NDISC_OPTION_RDNSS = 25,
SD_NDISC_OPTION_FLAGS_EXTENSION = 26,
SD_NDISC_OPTION_DNSSL = 31,
SD_NDISC_OPTION_CAPTIVE_PORTAL = 37
SD_NDISC_OPTION_CAPTIVE_PORTAL = 37,
SD_NDISC_OPTION_PREF64 = 38
};
/* Route preference, RFC 4191, Section 2.1 */
@ -85,14 +86,17 @@ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags);
int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime);
int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret);
/* Generic option access */
@ -100,28 +104,42 @@ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt);
int sd_ndisc_router_option_next(sd_ndisc_router *rt);
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type);
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size);
int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix_get_valid_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret);
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr);
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen);
int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_route_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret);
/* Specific option access: SD_NDISC_OPTION_RDNSS */
int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret);
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_rdnss_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_DNSSL */
int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret);
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_dnssl_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */
int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size);
/* Specific option access: SD_NDISC_OPTION_PREF64 */
int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret);
int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);

View file

@ -5,6 +5,7 @@ libnm_systemd_shared = static_library(
sources: files(
'nm-sd-utils-shared.c',
'src/basic/alloc-util.c',
'src/basic/btrfs.c',
'src/basic/env-file.c',
'src/basic/env-util.c',
'src/basic/escape.c',

View file

@ -105,6 +105,33 @@ void* greedy_realloc0(
return q;
}
void* greedy_realloc_append(
void **p,
size_t *n_p,
const void *from,
size_t n_from,
size_t size) {
uint8_t *q;
assert(p);
assert(n_p);
assert(from || n_from == 0);
if (n_from > SIZE_MAX - *n_p)
return NULL;
q = greedy_realloc(p, *n_p + n_from, size);
if (!q)
return NULL;
memcpy_safe(q + *n_p * size, from, n_from * size);
*n_p += n_from;
return q;
}
void *expand_to_usable(void *ptr, size_t newsize _unused_) {
return ptr;
}

View file

@ -15,13 +15,12 @@
typedef void (*free_func_t)(void *p);
typedef void* (*mfree_func_t)(void *p);
typedef void (*free_array_func_t)(void *p, size_t n);
/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
* proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
#define ALLOCA_MAX (4U*1024U*1024U)
#define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
#define new(t, n) ((t*) malloc_multiply(n, sizeof(t)))
#define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
@ -46,9 +45,9 @@ typedef void (*free_array_func_t)(void *p, size_t n);
(t*) alloca0((sizeof(t)*_n_)); \
})
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
#define newdup(t, p, n) ((t*) memdup_multiply(p, n, sizeof(t)))
#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, n, sizeof(t)))
#define malloc0(n) (calloc(1, (n) ?: 1))
@ -113,7 +112,7 @@ static inline bool size_multiply_overflow(size_t size, size_t need) {
return _unlikely_(need != 0 && size > (SIZE_MAX / need));
}
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) {
_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@ -129,7 +128,7 @@ _alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size
}
#endif
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) {
_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@ -138,7 +137,7 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si
/* Note that we can't decorate this function with _alloc_() since the returned memory area is one byte larger
* than the product of its parameters. */
static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
static inline void *memdup_suffix0_multiply(const void *p, size_t need, size_t size) {
if (size_multiply_overflow(size, need))
return NULL;
@ -147,6 +146,7 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
void* greedy_realloc(void **p, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t need, size_t size);
void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_from, size_t size);
#define GREEDY_REALLOC(array, need) \
greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
@ -154,6 +154,9 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
#define GREEDY_REALLOC0(array, need) \
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from) \
greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
#define alloca0(n) \
({ \
char *_new_; \
@ -224,7 +227,6 @@ static inline size_t malloc_sizeof_safe(void **xp) {
MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]), \
VOID_0))
/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
#define strdupa_safe(s) \
({ \
@ -235,7 +237,40 @@ static inline size_t malloc_sizeof_safe(void **xp) {
#define strndupa_safe(s, n) \
({ \
const char *_t = (s); \
(char*) memdupa_suffix0(_t, strnlen(_t, (n))); \
(char*) memdupa_suffix0(_t, strnlen(_t, n)); \
})
/* Free every element of the array. */
static inline void free_many(void **p, size_t n) {
assert(p || n == 0);
FOREACH_ARRAY(i, p, n)
*i = mfree(*i);
}
/* Typesafe wrapper for char** rather than void**. Unfortunately C won't implicitly cast this. */
static inline void free_many_charp(char **c, size_t n) {
free_many((void**) c, n);
}
_alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
size_t old_size;
void *q;
/* Like realloc(), but initializes anything appended to zero */
old_size = MALLOC_SIZEOF_SAFE(p);
q = realloc(p, new_size);
if (!q)
return NULL;
new_size = MALLOC_SIZEOF_SAFE(q); /* Update with actually allocated space */
if (new_size > old_size)
memset((uint8_t*) q + old_size, 0, new_size - old_size);
return q;
}
#include "memory-util.h"

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stddef.h>
const char *arphrd_to_name(int id);
int arphrd_from_name(const char *name);
size_t arphrd_to_hw_addr_len(uint16_t arphrd);

View file

@ -1,13 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
#include "macro.h"
int asynchronous_job(void* (*func)(void *p), void *arg);
int asynchronous_sync(pid_t *ret_pid);
int asynchronous_close(int fd);
DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close);

View file

@ -0,0 +1,100 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "nm-sd-adapt-shared.h"
#include <linux/btrfs.h>
#include <sys/ioctl.h>
#include "btrfs.h"
#include "fd-util.h"
#include "fs-util.h"
#include "path-util.h"
int btrfs_validate_subvolume_name(const char *name) {
if (!filename_is_valid(name))
return -EINVAL;
if (strlen(name) > BTRFS_SUBVOL_NAME_MAX)
return -E2BIG;
return 0;
}
static int extract_subvolume_name(const char *path, char **ret) {
_cleanup_free_ char *fn = NULL;
int r;
assert(path);
assert(ret);
r = path_extract_filename(path, &fn);
if (r < 0)
return r;
r = btrfs_validate_subvolume_name(fn);
if (r < 0)
return r;
*ret = TAKE_PTR(fn);
return 0;
}
int btrfs_subvol_make(int dir_fd, const char *path) {
struct btrfs_ioctl_vol_args args = {};
_cleanup_free_ char *subvolume = NULL, *parent = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(!isempty(path));
r = extract_subvolume_name(path, &subvolume);
if (r < 0)
return r;
r = path_extract_directory(path, &parent);
if (r < 0) {
if (r != -EDESTADDRREQ) /* Propagate error, unless only a filename was specified, which is OK */
return r;
dir_fd = fd_reopen_condition(dir_fd, O_CLOEXEC, O_PATH, &fd); /* drop O_PATH if it is set */
if (dir_fd < 0)
return dir_fd;
} else {
fd = openat(dir_fd, parent, O_DIRECTORY|O_RDONLY|O_CLOEXEC, 0);
if (fd < 0)
return -errno;
dir_fd = fd;
}
strncpy(args.name, subvolume, sizeof(args.name)-1);
return RET_NERRNO(ioctl(dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args));
}
int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode) {
mode_t old, combined;
int r;
assert(path);
/* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
old = umask(~mode);
combined = old | ~mode;
if (combined != ~mode)
umask(combined);
r = btrfs_subvol_make(dir_fd, path);
umask(old);
if (r >= 0)
return 1; /* subvol worked */
if (!ERRNO_IS_NOT_SUPPORTED(r))
return r;
if (mkdirat(dir_fd, path, mode) < 0)
return -errno;
return 0; /* plain directory */
}

View file

@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
int btrfs_validate_subvolume_name(const char *name);
int btrfs_subvol_make(int dir_fd, const char *path);
int btrfs_subvol_make_fallback(int dir_fd, const char *path, mode_t mode);

View file

@ -10,6 +10,7 @@
#include <sys/types.h>
#include "constants.h"
#include "pidref.h"
#include "set.h"
#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
@ -35,7 +36,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
/* The BPF hook implementing RestrictFileSystems= is not defined here.
* It's applied as late as possible in exec_child() so we don't block
* It's applied as late as possible in exec_invoke() so we don't block
* our own unit setup code. */
_CGROUP_CONTROLLER_MAX,
@ -66,10 +67,13 @@ typedef enum CGroupMask {
/* All real cgroup v2 controllers */
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All controllers we want to delegate in case of Delegate=yes. Which are pretty much the v2 controllers only, as delegation on v1 is not safe, and bpf stuff isn't a real controller */
CGROUP_MASK_DELEGATE = CGROUP_MASK_V2,
/* 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_RESTRICT_NETWORK_INTERFACES,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1,
} CGroupMask;
static inline CGroupMask CGROUP_MASK_EXTEND_JOINED(CGroupMask mask) {
@ -176,13 +180,13 @@ typedef enum CGroupUnified {
* generate paths with multiple adjacent / removed.
*/
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
int cg_read_pid(FILE *f, pid_t *_pid);
int cg_read_event(const char *controller, const char *path, const char *event,
char **val);
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
int cg_read_pid(FILE *f, pid_t *ret);
int cg_read_pidref(FILE *f, PidRef *ret);
int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
int cg_read_subgroup(DIR *d, char **fn);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
int cg_read_subgroup(DIR *d, char **ret);
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0,
@ -190,25 +194,31 @@ typedef enum CGroupFlags {
CGROUP_REMOVE = 1 << 2,
} CGroupFlags;
typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
typedef int (*cg_kill_log_func_t)(const PidRef *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_kill(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 *path);
int cg_kill_recursive(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);
int cg_mangle_path(const char *path, char **result);
int cg_mangle_path(const char *path, char **ret);
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs);
int cg_get_path(const char *controller, const char *path, const char *suffix, char **ret);
int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **ret);
int cg_pid_get_path(const char *controller, pid_t pid, char **path);
int cg_pid_get_path(const char *controller, pid_t pid, char **ret);
int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret);
int cg_rmdir(const char *controller, const char *path);
int cg_is_threaded(const char *controller, const char *path);
int cg_is_threaded(const char *path);
typedef enum {
int cg_is_delegated(const char *path);
int cg_is_delegated_fd(int fd);
int cg_has_coredump_receive(const char *path);
typedef enum {
CG_KEY_MODE_GRACEFUL = 1 << 0,
} CGroupKeyMode;
@ -239,14 +249,14 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
/* Does a parse_boolean() on the attribute contents and sets ret accordingly */
int cg_get_attribute_as_bool(const char *controller, const char *path, const char *attribute, bool *ret);
int cg_get_owner(const char *controller, const char *path, uid_t *ret_uid);
int cg_get_owner(const char *path, uid_t *ret_uid);
int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret);
int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
/* Returns negative on error, and 0 or 1 on success for the bool value */
int cg_get_xattr_bool(const char *controller, const char *path, const char *name);
int cg_remove_xattr(const char *controller, const char *path, const char *name);
int cg_get_xattr_bool(const char *path, const char *name);
int cg_remove_xattr(const char *path, const char *name);
int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);
@ -257,27 +267,28 @@ int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);
int cg_path_get_cgroupid(const char *path, uint64_t *ret);
int cg_path_get_session(const char *path, char **session);
int cg_path_get_owner_uid(const char *path, uid_t *uid);
int cg_path_get_unit(const char *path, char **unit);
int cg_path_get_unit_path(const char *path, char **unit);
int cg_path_get_user_unit(const char *path, char **unit);
int cg_path_get_machine_name(const char *path, char **machine);
int cg_path_get_slice(const char *path, char **slice);
int cg_path_get_user_slice(const char *path, char **slice);
int cg_path_get_session(const char *path, char **ret_session);
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid);
int cg_path_get_unit(const char *path, char **ret_unit);
int cg_path_get_unit_path(const char *path, char **ret_unit);
int cg_path_get_user_unit(const char *path, char **ret_unit);
int cg_path_get_machine_name(const char *path, char **ret_machine);
int cg_path_get_slice(const char *path, char **ret_slice);
int cg_path_get_user_slice(const char *path, char **ret_slice);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup);
int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_shifted);
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
int cg_pid_get_session(pid_t pid, char **session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *uid);
int cg_pid_get_unit(pid_t pid, char **unit);
int cg_pid_get_user_unit(pid_t pid, char **unit);
int cg_pid_get_machine_name(pid_t pid, char **machine);
int cg_pid_get_slice(pid_t pid, char **slice);
int cg_pid_get_user_slice(pid_t pid, char **slice);
int cg_pid_get_session(pid_t pid, char **ret_session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
int cg_pid_get_unit(pid_t pid, char **ret_unit);
int cg_pidref_get_unit(const PidRef *pidref, char **ret);
int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
int cg_pid_get_machine_name(pid_t pid, char **ret_machine);
int cg_pid_get_slice(pid_t pid, char **ret_slice);
int cg_pid_get_user_slice(pid_t pid, char **ret_slice);
int cg_path_decode_unit(const char *cgroup, char **unit);
int cg_path_decode_unit(const char *cgroup, char **ret_unit);
bool cg_needs_escape(const char *p);
int cg_escape(const char *p, char **ret);

View file

@ -59,22 +59,13 @@
#define NOTIFY_FD_MAX 768
#define NOTIFY_BUFFER_MAX PIPE_BUF
#if HAVE_SPLIT_USR
# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0"
# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n
#else
# define _CONF_PATHS_SPLIT_USR_NULSTR(n)
# define _CONF_PATHS_SPLIT_USR(n)
#endif
/* Return a nulstr for a standard cascade of configuration paths, suitable to pass to
* conf_files_list_nulstr() to implement drop-in directories for extending configuration files. */
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR_NULSTR(n)
"/usr/lib/" n "\0"
#define CONF_PATHS_USR(n) \
"/etc/" n, \
@ -83,8 +74,7 @@
"/usr/lib/" n
#define CONF_PATHS(n) \
CONF_PATHS_USR(n) \
_CONF_PATHS_SPLIT_USR(n)
CONF_PATHS_USR(n)
#define CONF_PATHS_USR_STRV(n) \
STRV_MAKE(CONF_PATHS_USR(n))
@ -99,14 +89,9 @@
* in containers so that our children inherit that. */
#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL)
#define PLYMOUTH_SOCKET { \
.un.sun_family = AF_UNIX, \
.un.sun_path = "\0/org/freedesktop/plymouthd", \
}
/* Path where PID1 listens for varlink subscriptions from systemd-oomd to notify of changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.system.ManagedOOM"
#define VARLINK_ADDR_PATH_MANAGED_OOM_SYSTEM "/run/systemd/io.systemd.ManagedOOM"
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.system.ManagedOOM"
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.systemd.ManagedOOM"
#define KERNEL_BASELINE_VERSION "4.15"

View file

@ -127,7 +127,7 @@ static int parse_env_file_internal(
state = VALUE;
if (!GREEDY_REALLOC(value, n_value+2))
return -ENOMEM;
return -ENOMEM;
value[n_value++] = c;
}
@ -245,7 +245,13 @@ static int parse_env_file_internal(
break;
case COMMENT_ESCAPE:
state = COMMENT;
log_debug("The line which doesn't begin with \";\" or \"#\", but follows a comment" \
" line trailing with escape is now treated as a non comment line since v254.");
if (strchr(NEWLINE, c)) {
state = PRE_KEY;
line++;
} else
state = COMMENT;
break;
}
}
@ -522,6 +528,7 @@ static int merge_env_file_push(
char ***env = ASSERT_PTR(userdata);
char *expanded_value;
int r;
assert(key);
@ -536,12 +543,12 @@ static int merge_env_file_push(
return 0;
}
expanded_value = replace_env(value, *env,
REPLACE_ENV_USE_ENVIRONMENT|
REPLACE_ENV_ALLOW_BRACELESS|
REPLACE_ENV_ALLOW_EXTENDED);
if (!expanded_value)
return -ENOMEM;
r = replace_env(value,
*env,
REPLACE_ENV_USE_ENVIRONMENT|REPLACE_ENV_ALLOW_BRACELESS|REPLACE_ENV_ALLOW_EXTENDED,
&expanded_value);
if (r < 0)
return log_error_errno(r, "%s:%u: Failed to expand variable '%s': %m", strna(filename), line, value);
free_and_replace(value, expanded_value);
@ -599,7 +606,7 @@ static void write_env_var(FILE *f, const char *v) {
fputc_unlocked('\n', f);
}
int write_env_file_at(int dir_fd, const char *fname, char **l) {
int write_env_file(int dir_fd, const char *fname, char **headers, char **l) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *p = NULL;
int r;
@ -613,6 +620,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) fchmod_umask(fileno(f), 0644);
STRV_FOREACH(i, headers) {
assert(isempty(*i) || startswith(*i, "#"));
fputs_unlocked(*i, f);
fputc_unlocked('\n', f);
}
STRV_FOREACH(i, l)
write_env_var(f, *i);
@ -627,4 +640,12 @@ int write_env_file_at(int dir_fd, const char *fname, char **l) {
(void) unlinkat(dir_fd, p, 0);
return r;
}
int write_vconsole_conf(int dir_fd, const char *fname, char **l) {
char **headers = STRV_MAKE(
"# Written by systemd-localed(8) or systemd-firstboot(1), read by systemd-localed",
"# and systemd-vconsole-setup(8). Use localectl(1) to update this file.");
return write_env_file(dir_fd, fname, headers, l);
}
#endif /* NM_IGNORED */

View file

@ -19,7 +19,6 @@ int load_env_file_pairs_fd(int fd, const char *fname, char ***ret);
int merge_env_file(char ***env, FILE *f, const char *fname);
int write_env_file_at(int dir_fd, const char *fname, char **l);
static inline int write_env_file(const char *fname, char **l) {
return write_env_file_at(AT_FDCWD, fname, l);
}
int write_env_file(int dir_fd, const char *fname, char **headers, char **l);
int write_vconsole_conf(int dir_fd, const char *fname, char **l);

View file

@ -29,20 +29,21 @@
"_"
static bool env_name_is_valid_n(const char *e, size_t n) {
if (!e)
return false;
if (n == SIZE_MAX)
n = strlen_ptr(e);
if (n <= 0)
return false;
assert(e);
if (ascii_isdigit(e[0]))
return false;
/* POSIX says the overall size of the environment block cannot
* be > ARG_MAX, an individual assignment hence cannot be
* either. Discounting the equal sign and trailing NUL this
* hence leaves ARG_MAX-2 as longest possible variable
* name. */
/* POSIX says the overall size of the environment block cannot be > ARG_MAX, an individual assignment
* hence cannot be either. Discounting the equal sign and trailing NUL this hence leaves ARG_MAX-2 as
* longest possible variable name. */
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
return false;
@ -246,9 +247,9 @@ static bool env_match(const char *t, const char *pattern) {
return true;
if (!strchr(pattern, '=')) {
size_t l = strlen(pattern);
t = startswith(t, pattern);
return strneq(t, pattern, l) && t[l] == '=';
return t && *t == '=';
}
return false;
@ -460,6 +461,35 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
return strv_env_replace_consume(l, p);
}
int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
int r;
assert(l);
assert(key);
if (!env_name_is_valid(key))
return -EINVAL;
if (!valuef) {
strv_env_unset(*l, key);
return 0;
}
_cleanup_free_ char *value = NULL;
va_list ap;
va_start(ap, valuef);
r = vasprintf(&value, valuef, ap);
va_end(ap);
if (r < 0)
return -ENOMEM;
char *p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
return strv_env_replace_consume(l, p);
}
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
@ -502,32 +532,31 @@ int _strv_env_assign_many(char ***l, ...) {
return 0;
}
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags) {
assert(name);
if (k == SIZE_MAX)
k = strlen(name);
if (k <= 0)
return NULL;
STRV_FOREACH_BACKWARDS(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
if (strneq(*i, name, k) && (*i)[k] == '=')
return (char*) *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
/* Safety check that the name is not overly long, before we do a stack allocation */
if (k > (size_t) sysconf(_SC_ARG_MAX) - 2)
return NULL;
t = strndupa_safe(name, k);
return getenv(t);
};
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
return strv_env_get_n(l, name, strlen(name), 0);
}
#endif /* NM_IGNORED */
char *strv_env_pairs_get(char **l, const char *name) {
@ -578,7 +607,61 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
static int strv_extend_with_length(char ***l, const char *s, size_t n) {
char *c;
c = strndup(s, n);
if (!c)
return -ENOMEM;
return strv_consume(l, c);
}
static int strv_env_get_n_validated(
char **env,
const char *name,
size_t l,
ReplaceEnvFlags flags,
char **ret, /* points into the env block! do not free! */
char ***unset_variables, /* updated in place */
char ***bad_variables) { /* ditto */
char *e;
int r;
assert(l == 0 || name);
assert(ret);
if (env_name_is_valid_n(name, l)) {
e = strv_env_get_n(env, name, l, flags);
if (!e && unset_variables) {
r = strv_extend_with_length(unset_variables, name, l);
if (r < 0)
return r;
}
} else {
e = NULL; /* Resolve invalid variable names the same way as unset ones */
if (bad_variables) {
r = strv_extend_with_length(bad_variables, name, l);
if (r < 0)
return r;
}
}
*ret = e;
return !!e;
}
int replace_env_full(
const char *format,
size_t n,
char **env,
ReplaceEnvFlags flags,
char **ret,
char ***ret_unset_variables,
char ***ret_bad_variables) {
enum {
WORD,
CURLY,
@ -589,15 +672,22 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
ALTERNATE_VALUE,
} state = WORD;
_cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
char *k;
_cleanup_free_ char *s = NULL;
char ***pu, ***pb, *k;
size_t i, len = 0; /* len is initialized to appease gcc */
int nest = 0;
int nest = 0, r;
assert(format);
for (e = format, i = 0; *e && i < n; e ++, i ++)
if (n == SIZE_MAX)
n = strlen(format);
pu = ret_unset_variables ? &unset_variables : NULL;
pb = ret_bad_variables ? &bad_variables : NULL;
for (e = format, i = 0; *e && i < n; e++, i++)
switch (state) {
case WORD:
@ -609,27 +699,28 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
if (*e == '{') {
k = strnappend(s, word, e-word-1);
if (!k)
return NULL;
return -ENOMEM;
free_and_replace(s, k);
word = e-1;
state = VARIABLE;
nest++;
} else if (*e == '$') {
k = strnappend(s, word, e-word);
if (!k)
return NULL;
return -ENOMEM;
free_and_replace(s, k);
word = e+1;
state = WORD;
} else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
} else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
k = strnappend(s, word, e-word-1);
if (!k)
return NULL;
return -ENOMEM;
free_and_replace(s, k);
@ -642,12 +733,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
case VARIABLE:
if (*e == '}') {
const char *t;
char *t;
t = strv_env_get_n(env, word+2, e-word-2, flags);
r = strv_env_get_n_validated(env, word+2, e-word-2, flags, &t, pu, pb);
if (r < 0)
return r;
if (!strextend(&s, t))
return NULL;
return -ENOMEM;
word = e+1;
state = WORD;
@ -689,18 +782,37 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
nest--;
if (nest == 0) {
const char *t;
_cleanup_strv_free_ char **u = NULL, **b = NULL;
_cleanup_free_ char *v = NULL;
char *t = NULL;
t = strv_env_get_n(env, word+2, len, flags);
r = strv_env_get_n_validated(env, word+2, len, flags, &t, pu, pb);
if (r < 0)
return r;
if (t && state == ALTERNATE_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
else if (!t && state == DEFAULT_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
if (t && state == ALTERNATE_VALUE) {
r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
if (r < 0)
return r;
t = v;
} else if (!t && state == DEFAULT_VALUE) {
r = replace_env_full(test_value, e-test_value, env, flags, &v, pu ? &u : NULL, pb ? &b : NULL);
if (r < 0)
return r;
t = v;
}
r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
if (r < 0)
return r;
r = strv_extend_strv(&bad_variables, b, /* filter_duplicates= */ true);
if (r < 0)
return r;
if (!strextend(&s, t))
return NULL;
return -ENOMEM;
word = e+1;
state = WORD;
@ -711,12 +823,14 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
const char *t;
char *t = NULL;
t = strv_env_get_n(env, word+1, e-word-1, flags);
r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
if (r < 0)
return r;
if (!strextend(&s, t))
return NULL;
return -ENOMEM;
word = e--;
i--;
@ -726,58 +840,83 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
}
if (state == VARIABLE_RAW) {
const char *t;
char *t;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
t = strv_env_get_n(env, word+1, e-word-1, flags);
return strjoin(s, t);
} else
return strnappend(s, word, e-word);
r = strv_env_get_n_validated(env, word+1, e-word-1, flags, &t, &unset_variables, &bad_variables);
if (r < 0)
return r;
if (!strextend(&s, t))
return -ENOMEM;
} else if (!strextendn(&s, word, e-word))
return -ENOMEM;
if (ret_unset_variables)
*ret_unset_variables = TAKE_PTR(unset_variables);
if (ret_bad_variables)
*ret_bad_variables = TAKE_PTR(bad_variables);
if (ret)
*ret = TAKE_PTR(s);
return 0;
}
char **replace_env_argv(char **argv, char **env) {
_cleanup_strv_free_ char **ret = NULL;
int replace_env_argv(
char **argv,
char **env,
char ***ret,
char ***ret_unset_variables,
char ***ret_bad_variables) {
_cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
size_t k = 0, l = 0;
int r;
l = strv_length(argv);
ret = new(char*, l+1);
if (!ret)
return NULL;
n = new(char*, l+1);
if (!n)
return -ENOMEM;
STRV_FOREACH(i, argv) {
const char *word = *i;
/* If $FOO appears as single word, replace it by the split up variable */
if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
char *e;
char **w;
if (word[0] == '$' && !IN_SET(word[1], '{', '$')) {
_cleanup_strv_free_ char **m = NULL;
const char *name = word + 1;
char *e, **w;
size_t q;
e = strv_env_get(env, *i+1);
if (e) {
int r;
r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
return NULL;
}
}
if (env_name_is_valid(name)) {
e = strv_env_get(env, name);
if (e)
r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
else if (ret_unset_variables)
r = strv_extend(&unset_variables, name);
else
r = 0;
} else if (ret_bad_variables)
r = strv_extend(&bad_variables, name);
else
r = 0;
if (r < 0)
return r;
q = strv_length(m);
l = l + q - 1;
w = reallocarray(ret, l + 1, sizeof(char *));
if (!w) {
ret[k] = NULL;
return NULL;
}
w = reallocarray(n, l + 1, sizeof(char*));
if (!w)
return -ENOMEM;
ret = w;
n = w;
if (m) {
memcpy(ret + k, m, q * sizeof(char*));
memcpy(n + k, m, (q + 1) * sizeof(char*));
m = mfree(m);
}
@ -785,15 +924,41 @@ char **replace_env_argv(char **argv, char **env) {
continue;
}
_cleanup_strv_free_ char **u = NULL, **b = NULL;
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
ret[k] = replace_env(*i, env, 0);
if (!ret[k])
return NULL;
k++;
r = replace_env_full(
word,
/* length= */ SIZE_MAX,
env,
/* flags= */ 0,
n + k,
ret_unset_variables ? &u : NULL,
ret_bad_variables ? &b : NULL);
if (r < 0)
return r;
n[++k] = NULL;
r = strv_extend_strv(&unset_variables, u, /* filter_duplicates= */ true);
if (r < 0)
return r;
r = strv_extend_strv(&bad_variables, b, /*filter_duplicates= */ true);
if (r < 0)
return r;
}
ret[k] = NULL;
return TAKE_PTR(ret);
if (ret_unset_variables) {
strv_uniq(strv_sort(unset_variables));
*ret_unset_variables = TAKE_PTR(unset_variables);
}
if (ret_bad_variables) {
strv_uniq(strv_sort(bad_variables));
*ret_bad_variables = TAKE_PTR(bad_variables);
}
*ret = TAKE_PTR(n);
return 0;
}
#endif /* NM_IGNORED */
@ -853,8 +1018,8 @@ int putenv_dup(const char *assignment, bool override) {
}
int setenv_systemd_exec_pid(bool update_only) {
char str[DECIMAL_STR_MAX(pid_t)];
const char *e;
int r;
/* Update $SYSTEMD_EXEC_PID=pid except when '*' is set for the variable. */
@ -865,10 +1030,9 @@ int setenv_systemd_exec_pid(bool update_only) {
if (streq_ptr(e, "*"))
return 0;
xsprintf(str, PID_FMT, getpid_cached());
if (setenv("SYSTEMD_EXEC_PID", str, 1) < 0)
return -errno;
r = setenvf("SYSTEMD_EXEC_PID", /* overwrite= */ 1, PID_FMT, getpid_cached());
if (r < 0)
return r;
return 1;
}
@ -944,4 +1108,45 @@ int getenv_steal_erase(const char *name, char **ret) {
return 1;
}
int set_full_environment(char **env) {
int r;
clearenv();
STRV_FOREACH(e, env) {
_cleanup_free_ char *k = NULL, *v = NULL;
r = split_pair(*e, "=", &k, &v);
if (r < 0)
return r;
if (setenv(k, v, /* overwrite= */ true) < 0)
return -errno;
}
return 0;
}
int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
_cleanup_free_ char *value = NULL;
va_list ap;
int r;
assert(name);
if (!valuef)
return RET_NERRNO(unsetenv(name));
va_start(ap, valuef);
DISABLE_WARNING_FORMAT_NONLITERAL;
r = vasprintf(&value, valuef, ap);
REENABLE_WARNING;
va_end(ap);
if (r < 0)
return -ENOMEM;
return RET_NERRNO(setenv(name, value, overwrite));
}
#endif /* NM_IGNORED */

View file

@ -19,19 +19,19 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
enum {
typedef enum ReplaceEnvFlags {
REPLACE_ENV_USE_ENVIRONMENT = 1 << 0,
REPLACE_ENV_ALLOW_BRACELESS = 1 << 1,
REPLACE_ENV_ALLOW_EXTENDED = 1 << 2,
};
} ReplaceEnvFlags;
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
static inline char *replace_env(const char *format, char **env, unsigned flags) {
return replace_env_n(format, strlen(format), env, flags);
int replace_env_full(const char *format, size_t n, char **env, ReplaceEnvFlags flags, char **ret, char ***ret_unset_variables, char ***ret_bad_variables);
static inline int replace_env(const char *format, char **env, ReplaceEnvFlags flags, char **ret) {
return replace_env_full(format, SIZE_MAX, env, flags, ret, NULL, NULL);
}
int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_variables, char ***ret_bad_variables);
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@ -49,11 +49,15 @@ 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);
int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
int _strv_env_assign_many(char ***l, ...) _sentinel_;
#define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlags flags);
static inline char* strv_env_get(char * const *x, const char *n) {
return strv_env_get_n(x, n, SIZE_MAX, 0);
}
char *strv_env_pairs_get(char **l, const char *name) _pure_;
int getenv_bool(const char *p);
@ -74,3 +78,7 @@ int setenv_systemd_exec_pid(bool update_only);
int getenv_path_list(const char *name, char ***ret_paths);
int getenv_steal_erase(const char *name, char **ret);
int set_full_environment(char **env);
int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);

View file

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
@ -73,6 +74,16 @@ static inline int RET_NERRNO(int ret) {
return ret;
}
/* Collect possible errors in <acc>, so that the first error can be returned.
* Returns (possibly updated) <acc>. */
#define RET_GATHER(acc, err) \
({ \
int *__a = &(acc), __e = (err); \
if (*__a >= 0 && __e < 0) \
*__a = __e; \
*__a; \
})
static inline int errno_or_else(int fallback) {
/* To be used when invoking library calls where errno handling is not defined clearly: we return
* errno if it is set, and the specified error otherwise. The idea is that the caller initializes
@ -84,12 +95,23 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback);
}
/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
#define _DEFINE_ABS_WRAPPER(name) \
static inline bool ERRNO_IS_##name(intmax_t r) { \
if (r == INTMAX_MIN) \
return false; \
return ERRNO_IS_NEG_##name(-imaxabs(r)); \
}
assert_cc(INT_MAX <= INTMAX_MAX);
/* For send()/recv() or read()/write(). */
static inline bool ERRNO_IS_TRANSIENT(int r) {
return IN_SET(abs(r),
EAGAIN,
EINTR);
static inline bool ERRNO_IS_NEG_TRANSIENT(intmax_t r) {
return IN_SET(r,
-EAGAIN,
-EINTR);
}
_DEFINE_ABS_WRAPPER(TRANSIENT);
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
@ -98,79 +120,87 @@ static inline bool ERRNO_IS_TRANSIENT(int r) {
*
* Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
* kernel tells us that with ETIMEDOUT, see tcp(7). */
static inline bool ERRNO_IS_DISCONNECT(int r) {
return IN_SET(abs(r),
ECONNABORTED,
ECONNREFUSED,
ECONNRESET,
EHOSTDOWN,
EHOSTUNREACH,
ENETDOWN,
ENETRESET,
ENETUNREACH,
ENONET,
ENOPROTOOPT,
ENOTCONN,
EPIPE,
EPROTO,
ESHUTDOWN,
ETIMEDOUT);
static inline bool ERRNO_IS_NEG_DISCONNECT(intmax_t r) {
return IN_SET(r,
-ECONNABORTED,
-ECONNREFUSED,
-ECONNRESET,
-EHOSTDOWN,
-EHOSTUNREACH,
-ENETDOWN,
-ENETRESET,
-ENETUNREACH,
-ENONET,
-ENOPROTOOPT,
-ENOTCONN,
-EPIPE,
-EPROTO,
-ESHUTDOWN,
-ETIMEDOUT);
}
_DEFINE_ABS_WRAPPER(DISCONNECT);
/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
* the accept(2) man page. */
static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
return ERRNO_IS_DISCONNECT(r) ||
ERRNO_IS_TRANSIENT(r) ||
abs(r) == EOPNOTSUPP;
static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(intmax_t r) {
return ERRNO_IS_NEG_DISCONNECT(r) ||
ERRNO_IS_NEG_TRANSIENT(r) ||
r == -EOPNOTSUPP;
}
_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
/* Resource exhaustion, could be our fault or general system trouble */
static inline bool ERRNO_IS_RESOURCE(int r) {
return IN_SET(abs(r),
EMFILE,
ENFILE,
ENOMEM);
static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
return IN_SET(r,
-EMFILE,
-ENFILE,
-ENOMEM);
}
_DEFINE_ABS_WRAPPER(RESOURCE);
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
return IN_SET(abs(r),
EOPNOTSUPP,
ENOTTY,
ENOSYS,
EAFNOSUPPORT,
EPFNOSUPPORT,
EPROTONOSUPPORT,
ESOCKTNOSUPPORT);
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
return IN_SET(r,
-EOPNOTSUPP,
-ENOTTY,
-ENOSYS,
-EAFNOSUPPORT,
-EPFNOSUPPORT,
-EPROTONOSUPPORT,
-ESOCKTNOSUPPORT);
}
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
/* Two different errors for access problems */
static inline bool ERRNO_IS_PRIVILEGE(int r) {
return IN_SET(abs(r),
EACCES,
EPERM);
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
return IN_SET(r,
-EACCES,
-EPERM);
}
_DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for "not enough disk space" */
static inline bool ERRNO_IS_DISK_SPACE(int r) {
return IN_SET(abs(r),
ENOSPC,
EDQUOT,
EFBIG);
static inline bool ERRNO_IS_NEG_DISK_SPACE(intmax_t r) {
return IN_SET(r,
-ENOSPC,
-EDQUOT,
-EFBIG);
}
_DEFINE_ABS_WRAPPER(DISK_SPACE);
/* Three different errors for "this device does not quite exist" */
static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
return IN_SET(abs(r),
ENODEV,
ENXIO,
ENOENT);
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
return IN_SET(r,
-ENODEV,
-ENXIO,
-ENOENT);
}
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
* where it simply doesn't have the requested xattr the same way */
static inline bool ERRNO_IS_XATTR_ABSENT(int r) {
return abs(r) == ENODATA ||
ERRNO_IS_NOT_SUPPORTED(r);
static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {
return r == -ENODATA ||
ERRNO_IS_NEG_NOT_SUPPORTED(r);
}
_DEFINE_ABS_WRAPPER(XATTR_ABSENT);

View file

@ -184,7 +184,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'u': {
/* C++11 style 16bit unicode */
/* C++11 style 16-bit unicode */
int a[4];
size_t i;
@ -211,7 +211,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
}
case 'U': {
/* C++11 style 32bit unicode */
/* C++11 style 32-bit unicode */
int a[8];
size_t i;
@ -474,6 +474,33 @@ char* octescape(const char *s, size_t len) {
return buf;
}
char* decescape(const char *s, const char *bad, size_t len) {
char *buf, *t;
/* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
assert(s || len == 0);
t = buf = new(char, len * 4 + 1);
if (!buf)
return NULL;
for (size_t i = 0; i < len; i++) {
uint8_t u = (uint8_t) s[i];
if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
*(t++) = '\\';
*(t++) = '0' + (u / 100);
*(t++) = '0' + ((u / 10) % 10);
*(t++) = '0' + (u % 10);
} else
*(t++) = u;
}
*t = 0;
return buf;
}
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
assert(bad);
assert(t);

View file

@ -65,6 +65,7 @@ static inline char* xescape(const char *s, const char *bad) {
return xescape_full(s, bad, SIZE_MAX, 0);
}
char* octescape(const char *s, size_t len);
char* decescape(const char *s, const char *bad, size_t len);
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
char* shell_escape(const char *s, const char *bad);

View file

@ -61,8 +61,8 @@ void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p);
assert(state);
siphash24_compress(&p->length, sizeof(p->length), state);
siphash24_compress(p->bytes, p->length, state);
siphash24_compress_typesafe(p->length, state);
siphash24_compress_safe(p->bytes, p->length, state);
}
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
@ -108,7 +108,7 @@ int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
}
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
siphash24_compress(p, sizeof(struct ether_addr), state);
siphash24_compress_typesafe(*p, state);
}
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
@ -272,3 +272,11 @@ int parse_ether_addr(const char *s, struct ether_addr *ret) {
*ret = a.ether;
return 0;
}
void ether_addr_mark_random(struct ether_addr *addr) {
assert(addr);
/* see eth_random_addr in the kernel */
addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
}

View file

@ -113,3 +113,5 @@ static inline bool ether_addr_is_global(const struct ether_addr *addr) {
extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;
void ether_addr_mark_random(struct ether_addr *addr);

View file

@ -275,11 +275,7 @@ int extract_many_words(const char **p, const char *separators, unsigned flags, .
r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
int j;
for (j = 0; j < c; j++)
free(l[j]);
free_many_charp(l, c);
return r;
}

View file

@ -94,11 +94,25 @@ void safe_close_pair(int p[static 2]) {
p[1] = safe_close(p[1]);
}
void close_many(const int fds[], size_t n_fd) {
assert(fds || n_fd <= 0);
void close_many(const int fds[], size_t n_fds) {
assert(fds || n_fds == 0);
for (size_t i = 0; i < n_fd; i++)
safe_close(fds[i]);
FOREACH_ARRAY(fd, fds, n_fds)
safe_close(*fd);
}
void close_many_unset(int fds[], size_t n_fds) {
assert(fds || n_fds == 0);
FOREACH_ARRAY(fd, fds, n_fds)
*fd = safe_close(*fd);
}
void close_many_and_free(int *fds, size_t n_fds) {
assert(fds || n_fds == 0);
close_many(fds, n_fds);
free(fds);
}
int fclose_nointr(FILE *f) {
@ -158,6 +172,19 @@ int fd_nonblock(int fd, bool nonblock) {
return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
}
int stdio_disable_nonblock(void) {
int ret = 0;
/* stdin/stdout/stderr really should have O_NONBLOCK, which would confuse apps if left on, as
* write()s might unexpectedly fail with EAGAIN. */
RET_GATHER(ret, fd_nonblock(STDIN_FILENO, false));
RET_GATHER(ret, fd_nonblock(STDOUT_FILENO, false));
RET_GATHER(ret, fd_nonblock(STDERR_FILENO, false));
return ret;
}
int fd_cloexec(int fd, bool cloexec) {
int flags, nflags;
@ -176,32 +203,32 @@ int fd_cloexec(int fd, bool cloexec) {
#if 0 /* NM_IGNORED */
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
int ret = 0, r;
int r = 0;
assert(n_fds == 0 || fds);
assert(fds || n_fds == 0);
for (size_t i = 0; i < n_fds; i++) {
if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
FOREACH_ARRAY(fd, fds, n_fds) {
if (*fd < 0) /* Skip gracefully over already invalidated fds */
continue;
r = fd_cloexec(fds[i], cloexec);
if (r < 0 && ret >= 0) /* Continue going, but return first error */
ret = r;
else
ret = 1; /* report if we did anything */
RET_GATHER(r, fd_cloexec(*fd, cloexec));
if (r >= 0)
r = 1; /* report if we did anything */
}
return ret;
return r;
}
_pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
assert(n_fdset == 0 || fdset);
static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
assert(fd >= 0);
assert(fds || n_fds == 0);
for (size_t i = 0; i < n_fdset; i++) {
if (fdset[i] < 0)
FOREACH_ARRAY(i, fds, n_fds) {
if (*i < 0)
continue;
if (fdset[i] == fd)
if (*i == fd)
return true;
}
@ -232,7 +259,7 @@ int get_max_fd(void) {
static int close_all_fds_frugal(const int except[], size_t n_except) {
int max_fd, r = 0;
assert(n_except == 0 || except);
assert(except || n_except == 0);
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
@ -247,8 +274,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
* spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
"Refusing to loop over %d potential fds.",
max_fd);
"Refusing to loop over %d potential fds.", max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
@ -257,8 +283,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
continue;
q = close_nointr(fd);
if (q < 0 && q != -EBADF && r >= 0)
r = q;
if (q != -EBADF)
RET_GATHER(r, q);
}
return r;
@ -589,7 +615,7 @@ int move_fd(int from, int to, int cloexec) {
if (fl < 0)
return -errno;
cloexec = !!(fl & FD_CLOEXEC);
cloexec = FLAGS_SET(fl, FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
@ -647,7 +673,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
original_output_fd,
original_error_fd },
null_fd = -EBADF, /* If we open /dev/null, we store the fd to it here */
copy_fd[3] = { -EBADF, -EBADF, -EBADF }, /* This contains all fds we duplicate here
copy_fd[3] = EBADF_TRIPLET, /* This contains all fds we duplicate here
* temporarily, and hence need to close at the end. */
r;
bool null_readable, null_writable;
@ -745,8 +771,7 @@ finish:
safe_close_above_stdio(original_error_fd);
/* Close the copies we moved > 2 */
for (int i = 0; i < 3; i++)
safe_close(copy_fd[i]);
close_many(copy_fd, 3);
/* Close our null fd, if it's > 2 */
safe_close_above_stdio(null_fd);
@ -756,9 +781,10 @@ finish:
#endif /* NM_IGNORED */
int fd_reopen(int fd, int flags) {
int new_fd, r;
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert(!FLAGS_SET(flags, O_CREAT));
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
* turn O_RDWR fds into O_RDONLY fds.
@ -782,19 +808,12 @@ int fd_reopen(int fd, int flags) {
* the same way as the non-O_DIRECTORY case. */
return -ELOOP;
if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD) {
if (FLAGS_SET(flags, O_DIRECTORY) || fd == AT_FDCWD)
/* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
* magic /proc/ directory, and make ourselves independent of that being mounted. */
new_fd = openat(fd, ".", flags | O_DIRECTORY);
if (new_fd < 0)
return -errno;
return RET_NERRNO(openat(fd, ".", flags | O_DIRECTORY));
return new_fd;
}
assert(fd >= 0);
new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
int new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
if (new_fd < 0) {
if (errno != ENOENT)
return -errno;
@ -811,7 +830,6 @@ int fd_reopen(int fd, int flags) {
return new_fd;
}
#if 0 /* NM_IGNORED */
int fd_reopen_condition(
int fd,
int flags,
@ -821,6 +839,7 @@ int fd_reopen_condition(
int r, new_fd;
assert(fd >= 0);
assert(!FLAGS_SET(flags, O_CREAT));
/* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified
* flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if
@ -843,6 +862,7 @@ int fd_reopen_condition(
return new_fd;
}
#if 0 /* NM_IGNORED */
int fd_is_opath(int fd) {
int r;
@ -901,34 +921,21 @@ int fd_get_diskseq(int fd, uint64_t *ret) {
}
int path_is_root_at(int dir_fd, const char *path) {
STRUCT_NEW_STATX_DEFINE(st);
STRUCT_NEW_STATX_DEFINE(pst);
_cleanup_close_ int fd = -EBADF;
int r;
_cleanup_close_ int fd = -EBADF, pfd = -EBADF;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
if (!isempty(path)) {
fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
fd = openat(dir_fd, path, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
return errno == ENOTDIR ? false : -errno;
dir_fd = fd;
}
r = statx_fallback(dir_fd, ".", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st.sx);
if (r == -ENOTDIR)
return false;
if (r < 0)
return r;
r = statx_fallback(dir_fd, "..", 0, STATX_TYPE|STATX_INO|STATX_MNT_ID, &pst.sx);
if (r < 0)
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
if (!statx_inode_same(&st.sx, &pst.sx))
return false;
pfd = openat(dir_fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC);
if (pfd < 0)
return errno == ENOTDIR ? false : -errno;
/* Even if the parent directory has the same inode, the fd may not point to the root directory "/",
* and we also need to check that the mount ids are the same. Otherwise, a construct like the
@ -936,40 +943,64 @@ int path_is_root_at(int dir_fd, const char *path) {
*
* $ mkdir /tmp/x /tmp/x/y
* $ mount --bind /tmp/x /tmp/x/y
*
* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
* kernel is used without /proc mounted. In that case, let's assume that we do not have such spurious
* mount points in an early boot stage, and silently skip the following check. */
*/
if (!FLAGS_SET(st.nsx.stx_mask, STATX_MNT_ID)) {
return fds_are_same_mount(dir_fd, pfd);
}
int fds_are_same_mount(int fd1, int fd2) {
STRUCT_NEW_STATX_DEFINE(st1);
STRUCT_NEW_STATX_DEFINE(st2);
int r;
assert(fd1 >= 0);
assert(fd2 >= 0);
r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
if (r < 0)
return r;
r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
if (r < 0)
return r;
/* First, compare inode. If these are different, the fd does not point to the root directory "/". */
if (!statx_inode_same(&st1.sx, &st2.sx))
return false;
/* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
* kernel is used. In that case, let's assume that we do not have such spurious mount points in an
* early boot stage, and silently skip the following check. */
if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
r = path_get_mnt_id_at(dir_fd, "", &mntid);
if (r == -ENOSYS)
r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
st.nsx.stx_mnt_id = mntid;
st.nsx.stx_mask |= STATX_MNT_ID;
st1.nsx.stx_mnt_id = mntid;
st1.nsx.stx_mask |= STATX_MNT_ID;
}
if (!FLAGS_SET(pst.nsx.stx_mask, STATX_MNT_ID)) {
if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
int mntid;
r = path_get_mnt_id_at(dir_fd, "..", &mntid);
if (r == -ENOSYS)
r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return true; /* skip the mount ID check */
if (r < 0)
return r;
assert(mntid >= 0);
pst.nsx.stx_mnt_id = mntid;
pst.nsx.stx_mask |= STATX_MNT_ID;
st2.nsx.stx_mnt_id = mntid;
st2.nsx.stx_mask |= STATX_MNT_ID;
}
return statx_mount_same(&st.nsx, &pst.nsx);
return statx_mount_same(&st1.nsx, &st2.nsx);
}
const char *accmode_to_string(int flags) {
@ -984,4 +1015,12 @@ const char *accmode_to_string(int flags) {
return NULL;
}
}
char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) {
assert(buf);
assert(fd >= 0);
assert(pid >= 0);
assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd));
return buf;
}
#endif /* NM_IGNORED */

View file

@ -16,7 +16,10 @@
/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
#define PIPE_EBADF { -EBADF, -EBADF }
/* Useful helpers for initializing pipe(), socketpair() or stdio fd arrays */
#define EBADF_PAIR { -EBADF, -EBADF }
#define EBADF_TRIPLET { -EBADF, -EBADF, -EBADF }
int close_nointr(int fd);
int safe_close(int fd);
@ -29,7 +32,9 @@ static inline int safe_close_above_stdio(int fd) {
return safe_close(fd);
}
void close_many(const int fds[], size_t n_fd);
void close_many(const int fds[], size_t n_fds);
void close_many_unset(int fds[], size_t n_fds);
void close_many_and_free(int *fds, size_t n_fds);
int fclose_nointr(FILE *f);
FILE* safe_fclose(FILE *f);
@ -47,6 +52,11 @@ static inline void fclosep(FILE **f) {
safe_fclose(*f);
}
static inline void* close_fd_ptr(void *p) {
safe_close(PTR_TO_FD(p));
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(FILE*, pclose, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
@ -57,6 +67,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
#define _cleanup_close_pair_ _cleanup_(close_pairp)
int fd_nonblock(int fd, bool nonblock);
int stdio_disable_nonblock(void);
int fd_cloexec(int fd, bool cloexec);
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);
@ -102,6 +114,9 @@ int read_nr_open(void);
int fd_get_diskseq(int fd, uint64_t *ret);
int path_is_root_at(int dir_fd, const char *path);
static inline int path_is_root(const char *path) {
return path_is_root_at(AT_FDCWD, path);
}
static inline int dir_fd_is_root(int dir_fd) {
return path_is_root_at(dir_fd, NULL);
}
@ -109,6 +124,8 @@ static inline int dir_fd_is_root_or_cwd(int dir_fd) {
return dir_fd == AT_FDCWD ? true : path_is_root_at(dir_fd, NULL);
}
int fds_are_same_mount(int fd1, int fd2);
/* 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))
@ -123,6 +140,16 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f
#define FORMAT_PROC_FD_PATH(fd) \
format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
/* The maximum length a buffer for a /proc/<pid>/fd/<fd> path needs */
#define PROC_PID_FD_PATH_MAX \
(STRLEN("/proc//fd/") + DECIMAL_STR_MAX(pid_t) + DECIMAL_STR_MAX(int))
char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd);
/* Kinda the same as FORMAT_PROC_FD_PATH(), but goes by PID rather than "self" symlink */
#define FORMAT_PROC_PID_FD_PATH(pid, fd) \
format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd))
const char *accmode_to_string(int flags);
/* Like ASSERT_PTR, but for fds */

View file

@ -30,10 +30,13 @@
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
#include "terminal-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)
/* Used when a size is specified for read_full_file() with READ_FULL_FILE_UNBASE64 or _UNHEX */
#define READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY 3
/* 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
@ -200,6 +203,19 @@ int write_string_stream_ts(
return 0;
}
static mode_t write_string_file_flags_to_mode(WriteStringFileFlags flags) {
/* We support three different modes, that are the ones that really make sense for text files like this:
*
* 0600 (i.e. root-only)
* 0444 (i.e. read-only)
* 0644 (i.e. writable for root, readable for everyone else)
*/
return FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 :
FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0444) ? 0444 : 0644;
}
static int write_string_file_atomic_at(
int dir_fd,
const char *fn,
@ -225,7 +241,7 @@ static int write_string_file_atomic_at(
if (r < 0)
goto fail;
r = fchmod_umask(fileno(f), FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0644);
r = fchmod_umask(fileno(f), write_string_file_flags_to_mode(flags));
if (r < 0)
goto fail;
@ -288,7 +304,7 @@ int write_string_file_ts_at(
(FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
(FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
(FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666));
write_string_file_flags_to_mode(flags));
if (fd < 0) {
r = -errno;
goto fail;
@ -576,7 +592,7 @@ int read_full_stream_full(
size_t *ret_size) {
_cleanup_free_ char *buf = NULL;
size_t n, n_next = 0, l;
size_t n, n_next = 0, l, expected_decoded_size = size;
int fd, r;
assert(f);
@ -587,6 +603,13 @@ int read_full_stream_full(
if (offset != UINT64_MAX && offset > LONG_MAX) /* fseek() can only deal with "long" offsets */
return -ERANGE;
if ((flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) != 0) {
if (size <= SIZE_MAX / READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY)
size *= READ_FULL_FILE_ENCODED_STRING_AMPLIFICATION_BOUNDARY;
else
size = SIZE_MAX;
}
fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
* fmemopen()), let's optimize our buffering */
@ -711,6 +734,11 @@ int read_full_stream_full(
explicit_bzero_safe(buf, n);
free_and_replace(buf, decoded);
n = l = decoded_size;
if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && l > expected_decoded_size) {
r = -E2BIG;
goto finalize;
}
}
if (!ret_size) {
@ -1057,7 +1085,9 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
if (mode_flags < 0)
return mode_flags;
copy_fd = fd_reopen(fd, mode_flags);
/* Flags returned by fopen_mode_to_flags might contain O_CREAT, but it doesn't make sense for fd_reopen
* since we're working on an existing fd anyway. Let's drop it here to avoid triggering assertion. */
copy_fd = fd_reopen(fd, mode_flags & ~O_CREAT);
if (copy_fd < 0)
return copy_fd;
@ -1069,123 +1099,171 @@ int fdopen_independent(int fd, const char *mode, FILE **ret) {
return 0;
}
static int search_and_fopen_internal(
static int search_and_open_internal(
const char *path,
const char *mode,
int mode, /* if ret_fd is NULL this is an [FRWX]_OK mode for access(), otherwise an open mode for open() */
const char *root,
char **search,
FILE **ret,
int *ret_fd,
char **ret_path) {
int r;
assert(!ret_fd || !FLAGS_SET(mode, O_CREAT)); /* We don't support O_CREAT for this */
assert(path);
assert(mode);
assert(ret);
if (path_is_absolute(path)) {
_cleanup_close_ int fd = -EBADF;
if (ret_fd)
/* We only specify 0777 here to appease static analyzers, it's never used since we
* don't support O_CREAT here */
r = fd = RET_NERRNO(open(path, mode, 0777));
else
r = RET_NERRNO(access(path, mode));
if (r < 0)
return r;
if (ret_path) {
r = path_simplify_alloc(path, ret_path);
if (r < 0)
return r;
}
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (!path_strv_resolve_uniq(search, root))
return -ENOMEM;
STRV_FOREACH(i, search) {
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *p = NULL;
FILE *f;
p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
f = fopen(p, mode);
if (f) {
if (ret_fd)
/* as above, 0777 is static analyzer appeasement */
r = fd = RET_NERRNO(open(p, mode, 0777));
else
r = RET_NERRNO(access(p, F_OK));
if (r >= 0) {
if (ret_path)
*ret_path = path_simplify(TAKE_PTR(p));
*ret = f;
if (ret_fd)
*ret_fd = TAKE_FD(fd);
return 0;
}
if (errno != ENOENT)
return -errno;
if (r != -ENOENT)
return r;
}
return -ENOENT;
}
int search_and_fopen(
const char *filename,
const char *mode,
int search_and_open(
const char *path,
int mode,
const char *root,
const char **search,
FILE **ret,
char **search,
int *ret_fd,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
assert(filename);
assert(mode);
assert(ret);
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
f = fopen(filename, mode);
if (!f)
return -errno;
if (ret_path) {
char *p;
p = strdup(filename);
if (!p)
return -ENOMEM;
*ret_path = path_simplify(p);
}
*ret = TAKE_PTR(f);
return 0;
}
assert(path);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(filename, mode, root, copy, ret, ret_path);
return search_and_open_internal(path, mode, root, copy, ret_fd, ret_path);
}
int search_and_fopen_nulstr(
const char *filename,
static int search_and_fopen_internal(
const char *path,
const char *mode,
const char *root,
const char *search,
FILE **ret,
char **search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **s = NULL;
_cleanup_free_ char *found_path = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
if (path_is_absolute(filename)) {
_cleanup_fclose_ FILE *f = NULL;
assert(path);
assert(mode || !ret_file);
f = fopen(filename, mode);
r = search_and_open(
path,
mode ? fopen_mode_to_flags(mode) : 0,
root,
search,
ret_file ? &fd : NULL,
ret_path ? &found_path : NULL);
if (r < 0)
return r;
if (ret_file) {
FILE *f = take_fdopen(&fd, mode);
if (!f)
return -errno;
if (ret_path) {
char *p;
p = strdup(filename);
if (!p)
return -ENOMEM;
*ret_path = path_simplify(p);
}
*ret = TAKE_PTR(f);
return 0;
*ret_file = f;
}
s = strv_split_nulstr(search);
if (!s)
if (ret_path)
*ret_path = TAKE_PTR(found_path);
return 0;
}
int search_and_fopen(
const char *path,
const char *mode,
const char *root,
const char **search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **copy = NULL;
assert(path);
assert(mode || !ret_file);
copy = strv_copy((char**) search);
if (!copy)
return -ENOMEM;
return search_and_fopen_internal(filename, mode, root, s, ret, ret_path);
return search_and_fopen_internal(path, mode, root, copy, ret_file, ret_path);
}
int search_and_fopen_nulstr(
const char *path,
const char *mode,
const char *root,
const char *search,
FILE **ret_file,
char **ret_path) {
_cleanup_strv_free_ char **l = NULL;
assert(path);
assert(mode || !ret_file);
l = strv_split_nulstr(search);
if (!l)
return -ENOMEM;
return search_and_fopen_internal(path, mode, root, l, ret_file, ret_path);
}
#endif /* NM_IGNORED */
@ -1259,33 +1337,31 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
}
#endif /* NM_IGNORED */
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
int r;
int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space) {
assert(s);
assert(space);
/* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
* when specified shall initially point to a boolean variable initialized to false. It is set to true after the
* first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
* element, but not before the first one. */
/* Outputs the specified string with fputs(), but optionally prefixes it with a separator.
* The *space parameter when specified shall initially point to a boolean variable initialized
* to false. It is set to true after the first invocation. This call is supposed to be use in loops,
* where a separator shall be inserted between each element, but not before the first one. */
if (!f)
f = stdout;
if (space) {
if (!separator)
separator = " ";
if (!separator)
separator = " ";
if (*space) {
r = fputs(separator, f);
if (r < 0)
return r;
}
if (*space)
if (fputs(separator, f) < 0)
return -EIO;
*space = true;
}
*space = true;
return fputs(s, f);
if (fputs(s, f) < 0)
return -EIO;
return 0;
}
#if 0 /* NM_IGNORED */
@ -1406,7 +1482,7 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
* and don't call isatty() on an invalid fd */
flags |= READ_LINE_NOT_A_TTY;
else
flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
@ -1437,6 +1513,36 @@ int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret) {
return (int) count;
}
int read_stripped_line(FILE *f, size_t limit, char **ret) {
_cleanup_free_ char *s = NULL;
int r;
assert(f);
r = read_line(f, limit, ret ? &s : NULL);
if (r < 0)
return r;
if (ret) {
const char *p;
p = strstrip(s);
if (p == s)
*ret = TAKE_PTR(s);
else {
char *copy;
copy = strdup(p);
if (!copy)
return -ENOMEM;
*ret = copy;
}
}
return r;
}
int safe_fgetc(FILE *f, char *ret) {
int k;

View file

@ -26,7 +26,8 @@ typedef enum {
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,
WRITE_STRING_FILE_MODE_0444 = 1 << 11,
WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 12,
/* 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()
@ -129,8 +130,12 @@ static inline int fopen_unlocked(const char *path, const char *mode, FILE **ret)
int fdopen_independent(int fd, const char *mode, FILE **ret);
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 search_and_open(const char *path, int mode, const char *root, char **search, int *ret_fd, char **ret_path);
static inline int search_and_access(const char *path, int mode, const char *root, char**search, char **ret_path) {
return search_and_open(path, mode, root, search, NULL, ret_path);
}
int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret_file, char **ret_path);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret_file, char **ret_path);
int fflush_and_check(FILE *f);
int fflush_sync_and_check(FILE *f);
@ -138,7 +143,7 @@ int fflush_sync_and_check(FILE *f);
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
int fputs_with_separator(FILE *f, const char *s, const char *separator, bool *space);
typedef enum ReadLineFlags {
READ_LINE_ONLY_NUL = 1 << 0,
@ -162,6 +167,8 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
return read_line_full(f, limit, READ_LINE_ONLY_NUL, ret);
}
int read_stripped_line(FILE *f, size_t limit, char **ret);
int safe_fgetc(FILE *f, char *ret);
int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);

View file

@ -11,6 +11,7 @@
#include <unistd.h>
#include "alloc-util.h"
#include "btrfs.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
@ -292,8 +293,22 @@ int fchmod_umask(int fd, mode_t m) {
int fchmod_opath(int fd, mode_t m) {
/* 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. */
* O_PATH. The tool set we have is non-intuitive:
* - fchmod(2) only operates on open files (i. e., fds with an open file description);
* - fchmodat(2) does not have a flag arg like fchownat(2) does, so no way to pass AT_EMPTY_PATH;
* + it should not be confused with the libc fchmodat(3) interface, which adds 4th flag argument,
* but does not support AT_EMPTY_PATH (only supports AT_SYMLINK_NOFOLLOW);
* - fchmodat2(2) supports all the AT_* flags, but is still very recent.
*
* We try to use fchmodat2(), and, if it is not supported, resort
* to the /proc/self/fd dance. */
assert(fd >= 0);
if (fchmodat2(fd, "", m, AT_EMPTY_PATH) >= 0)
return 0;
if (!IN_SET(errno, ENOSYS, EPERM)) /* Some container managers block unknown syscalls with EPERM */
return -errno;
if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
if (errno != ENOENT)
@ -1108,6 +1123,16 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
/* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
*
* O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
* opened. When used with the XO_SUBVOLUME flag this will even create a btrfs subvolume.
*
* If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
*
* If the path is specified NULL or empty, behaves like fd_reopen().
*/
if (isempty(path)) {
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
@ -1120,7 +1145,10 @@ int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags
}
if (FLAGS_SET(open_flags, O_DIRECTORY|O_CREAT)) {
r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (FLAGS_SET(xopen_flags, XO_SUBVOLUME))
r = btrfs_subvol_make_fallback(dir_fd, path, mode);
else
r = RET_NERRNO(mkdirat(dir_fd, path, mode));
if (r == -EEXIST) {
if (FLAGS_SET(open_flags, O_EXCL))
return -EEXIST;
@ -1183,12 +1211,11 @@ int xopenat_lock(
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(path);
assert(IN_SET(operation & ~LOCK_NB, LOCK_EX, LOCK_SH));
/* POSIX/UNPOSIX locks don't work on directories (errno is set to -EBADF so let's return early with
* the same error here). */
if (FLAGS_SET(open_flags, O_DIRECTORY) && locktype != LOCK_BSD)
if (FLAGS_SET(open_flags, O_DIRECTORY) && !IN_SET(locktype, LOCK_BSD, LOCK_NONE))
return -EBADF;
for (;;) {

View file

@ -133,7 +133,8 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
typedef enum XOpenFlags {
XO_LABEL = 1 << 0,
XO_LABEL = 1 << 0,
XO_SUBVOLUME = 1 << 1,
} XOpenFlags;
int xopenat(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_flags, mode_t mode);

View file

@ -25,7 +25,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled;
}
const char *special_glyph(SpecialGlyph code) {
const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
* conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@ -54,11 +54,12 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = "-",
[SPECIAL_GLYPH_LIGHT_SHADE] = "-",
[SPECIAL_GLYPH_DARK_SHADE] = "X",
[SPECIAL_GLYPH_FULL_BLOCK] = "#",
[SPECIAL_GLYPH_SIGMA] = "S",
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ARROW_UP] = "^",
[SPECIAL_GLYPH_ARROW_DOWN] = "v",
[SPECIAL_GLYPH_ARROW_LEFT] = "<-",
[SPECIAL_GLYPH_ARROW_RIGHT] = "->",
[SPECIAL_GLYPH_ELLIPSIS] = "...",
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]",
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
@ -73,6 +74,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
@ -98,6 +100,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_CROSS_MARK] = u8"", /* actually called: BALLOT X */
[SPECIAL_GLYPH_LIGHT_SHADE] = u8"",
[SPECIAL_GLYPH_DARK_SHADE] = u8"",
[SPECIAL_GLYPH_FULL_BLOCK] = u8"",
[SPECIAL_GLYPH_SIGMA] = u8"Σ",
[SPECIAL_GLYPH_ARROW_UP] = u8"", /* actually called: UPWARDS ARROW */
[SPECIAL_GLYPH_ARROW_DOWN] = u8"", /* actually called: DOWNWARDS ARROW */
@ -131,7 +134,10 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"",
[SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫",
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
[SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽",
[SPECIAL_GLYPH_WORLD] = u8"🌍",
},
};
@ -139,5 +145,5 @@ const char *special_glyph(SpecialGlyph code) {
return NULL;
assert(code < _SPECIAL_GLYPH_MAX);
return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
}

View file

@ -22,14 +22,15 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_MU,
SPECIAL_GLYPH_CHECK_MARK,
SPECIAL_GLYPH_CROSS_MARK,
SPECIAL_GLYPH_ARROW_LEFT,
SPECIAL_GLYPH_ARROW_RIGHT,
SPECIAL_GLYPH_ARROW_UP,
SPECIAL_GLYPH_ARROW_DOWN,
SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_LIGHT_SHADE,
SPECIAL_GLYPH_DARK_SHADE,
SPECIAL_GLYPH_FULL_BLOCK,
SPECIAL_GLYPH_SIGMA,
SPECIAL_GLYPH_ARROW_UP,
SPECIAL_GLYPH_ARROW_DOWN,
SPECIAL_GLYPH_ARROW_LEFT,
SPECIAL_GLYPH_ARROW_RIGHT,
SPECIAL_GLYPH_ELLIPSIS,
SPECIAL_GLYPH_EXTERNAL_LINK,
_SPECIAL_GLYPH_FIRST_EMOJI,
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
@ -44,15 +45,22 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
SPECIAL_GLYPH_LOW_BATTERY,
SPECIAL_GLYPH_WARNING_SIGN,
SPECIAL_GLYPH_COMPUTER_DISK,
SPECIAL_GLYPH_WORLD,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
const char *special_glyph(SpecialGlyph code) _const_;
bool emoji_enabled(void);
const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
static inline const char *special_glyph(SpecialGlyph code) {
return special_glyph_full(code, false);
}
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}

View file

@ -36,7 +36,7 @@ void path_hash_func(const char *q, struct siphash *state) {
/* if path is absolute, add one "/" to the hash. */
if (path_is_absolute(q))
siphash24_compress("/", 1, state);
siphash24_compress_byte('/', state);
for (;;) {
const char *e;
@ -71,7 +71,7 @@ DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
#endif /* NM_IGNORED */
void trivial_hash_func(const void *p, struct siphash *state) {
siphash24_compress(&p, sizeof(p), state);
siphash24_compress_typesafe(p, state);
}
int trivial_compare_func(const void *a, const void *b) {
@ -97,7 +97,7 @@ const struct hash_ops trivial_hash_ops_free_free = {
};
void uint64_hash_func(const uint64_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(uint64_t), state);
siphash24_compress_typesafe(*p, state);
}
int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
@ -109,7 +109,7 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func
#if 0 /* NM_IGNORED */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state) {
siphash24_compress(p, sizeof(dev_t), state);
siphash24_compress_typesafe(*p, state);
}
#endif

View file

@ -93,14 +93,14 @@ extern const struct hash_ops trivial_hash_ops;
extern const struct hash_ops trivial_hash_ops_free;
extern const struct hash_ops trivial_hash_ops_free_free;
/* 32bit values we can always just embed in the pointer itself, but in order to support 32bit archs we need store 64bit
/* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
* values indirectly, since they don't fit in a pointer. */
void uint64_hash_func(const uint64_t *p, struct siphash *state);
int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
extern const struct hash_ops uint64_hash_ops;
/* On some archs dev_t is 32bit, and on others 64bit. And sometimes it's 64bit on 32bit archs, and sometimes 32bit on
* 64bit archs. Yuck! */
/* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
* 64-bit archs. Yuck! */
#if SIZEOF_DEV_T != 8
void devt_hash_func(const dev_t *p, struct siphash *state);
#else

View file

@ -23,6 +23,7 @@
#include "random-util.h"
#include "set.h"
#include "siphash24.h"
#include "sort-util.h"
#include "string-util.h"
#include "strv.h"
@ -176,9 +177,9 @@ struct _packed_ indirect_storage {
};
struct direct_storage {
/* This gives us 39 bytes on 64bit, or 35 bytes on 32bit.
* That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit,
* or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */
/* This gives us 39 bytes on 64-bit, or 35 bytes on 32-bit.
* That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64-bit,
* or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32-bit. */
uint8_t storage[sizeof(struct indirect_storage)];
};
@ -2112,3 +2113,54 @@ bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needl
return set_fnmatch_one(include_patterns, needle);
}
static int hashmap_entry_compare(
struct hashmap_base_entry * const *a,
struct hashmap_base_entry * const *b,
compare_func_t compare) {
assert(a && *a);
assert(b && *b);
assert(compare);
return compare((*a)->key, (*b)->key);
}
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n) {
_cleanup_free_ struct hashmap_base_entry **entries = NULL;
Iterator iter;
unsigned idx;
size_t n = 0;
assert(ret);
if (_hashmap_size(h) == 0) {
*ret = NULL;
if (ret_n)
*ret_n = 0;
return 0;
}
/* We append one more element than needed so that the resulting array can be used as a strv. We
* don't count this entry in the returned size. */
entries = new(struct hashmap_base_entry*, _hashmap_size(h) + 1);
if (!entries)
return -ENOMEM;
HASHMAP_FOREACH_IDX(idx, h, iter)
entries[n++] = bucket_at(h, idx);
assert(n == _hashmap_size(h));
entries[n] = NULL;
typesafe_qsort_r(entries, n, hashmap_entry_compare, h->hash_ops->compare);
/* Reuse the array. */
FOREACH_ARRAY(e, entries, n)
*e = entry_value(h, *e);
*ret = (void**) TAKE_PTR(entries);
if (ret_n)
*ret_n = n;
return 0;
}

View file

@ -398,12 +398,28 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
return _hashmap_get_strv(HASHMAP_BASE(h));
}
int _hashmap_dump_sorted(HashmapBase *h, void ***ret, size_t *ret_n);
static inline int hashmap_dump_sorted(Hashmap *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
static inline int ordered_hashmap_dump_sorted(OrderedHashmap *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
static inline int set_dump_sorted(Set *h, void ***ret, size_t *ret_n) {
return _hashmap_dump_sorted(HASHMAP_BASE(h), ret, ret_n);
}
/*
* Hashmaps are iterated in unpredictable order.
* OrderedHashmaps are an exception to this. They are iterated in the order
* the entries were inserted.
* It is safe to remove the current entry.
*/
#define _HASHMAP_BASE_FOREACH(e, h, i) \
for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), NULL); )
#define HASHMAP_BASE_FOREACH(e, h) \
_HASHMAP_BASE_FOREACH(e, h, UNIQ_T(i, UNIQ))
#define _HASHMAP_FOREACH(e, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); )
#define HASHMAP_FOREACH(e, h) \
@ -414,6 +430,11 @@ static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
#define ORDERED_HASHMAP_FOREACH(e, h) \
_ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
#define _HASHMAP_BASE_FOREACH_KEY(e, k, h, i) \
for (Iterator i = ITERATOR_FIRST; _hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
#define HASHMAP_BASE_FOREACH_KEY(e, k, h) \
_HASHMAP_BASE_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
#define _HASHMAP_FOREACH_KEY(e, k, h, i) \
for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
#define HASHMAP_FOREACH_KEY(e, k, h) \

View file

@ -116,7 +116,7 @@ int unhexmem_full(
const char *p,
size_t l,
bool secure,
void **ret,
void **ret_data,
size_t *ret_len) {
_cleanup_free_ uint8_t *buf = NULL;
@ -157,8 +157,8 @@ int unhexmem_full(
if (ret_len)
*ret_len = (size_t) (z - buf);
if (ret)
*ret = TAKE_PTR(buf);
if (ret_data)
*ret_data = TAKE_PTR(buf);
return 0;
}
@ -557,12 +557,12 @@ int unbase64char(char c) {
offset += '9' - '0' + 1;
if (c == '+')
if (IN_SET(c, '+', '-')) /* Support both the regular and the URL safe character set (see above) */
return offset;
offset++;
if (c == '/')
if (IN_SET(c, '/', '_')) /* ditto */
return offset;
return -EINVAL;
@ -772,7 +772,7 @@ int unbase64mem_full(
const char *p,
size_t l,
bool secure,
void **ret,
void **ret_data,
size_t *ret_size) {
_cleanup_free_ uint8_t *buf = NULL;
@ -860,8 +860,8 @@ int unbase64mem_full(
if (ret_size)
*ret_size = (size_t) (z - buf);
if (ret)
*ret = TAKE_PTR(buf);
if (ret_data)
*ret_data = TAKE_PTR(buf);
return 0;
}

View file

@ -18,9 +18,9 @@ char hexchar(int x) _const_;
int unhexchar(char c) _const_;
char *hexmem(const void *p, size_t l);
int unhexmem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
return unhexmem_full(p, l, false, mem, len);
int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
char base32hexchar(int x) _const_;
@ -45,9 +45,9 @@ ssize_t base64_append(
size_t l,
size_t margin,
size_t width);
int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
return unbase64mem_full(p, l, false, mem, len);
int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
}
void hexdump(FILE *f, const void *p, size_t s);

View file

@ -736,10 +736,11 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen)
}
}
int in4_addr_prefix_covers(
int in4_addr_prefix_covers_full(
const struct in_addr *prefix,
unsigned char prefixlen,
const struct in_addr *address) {
const struct in_addr *address,
unsigned char address_prefixlen) {
struct in_addr masked_prefix, masked_address;
int r;
@ -747,6 +748,9 @@ int in4_addr_prefix_covers(
assert(prefix);
assert(address);
if (prefixlen > address_prefixlen)
return false;
masked_prefix = *prefix;
r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@ -760,10 +764,11 @@ int in4_addr_prefix_covers(
return in4_addr_equal(&masked_prefix, &masked_address);
}
int in6_addr_prefix_covers(
int in6_addr_prefix_covers_full(
const struct in6_addr *prefix,
unsigned char prefixlen,
const struct in6_addr *address) {
const struct in6_addr *address,
unsigned char address_prefixlen) {
struct in6_addr masked_prefix, masked_address;
int r;
@ -771,6 +776,9 @@ int in6_addr_prefix_covers(
assert(prefix);
assert(address);
if (prefixlen > address_prefixlen)
return false;
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@ -784,20 +792,21 @@ int in6_addr_prefix_covers(
return in6_addr_equal(&masked_prefix, &masked_address);
}
int in_addr_prefix_covers(
int in_addr_prefix_covers_full(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
const union in_addr_union *address,
unsigned char address_prefixlen) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
return in4_addr_prefix_covers_full(&prefix->in, prefixlen, &address->in, address_prefixlen);
case AF_INET6:
return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
return in6_addr_prefix_covers_full(&prefix->in6, prefixlen, &address->in6, address_prefixlen);
default:
return -EAFNOSUPPORT;
}
@ -922,12 +931,19 @@ int in_addr_prefix_from_string_auto_internal(
}
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
assert(u);
assert(state);
siphash24_compress(u->bytes, FAMILY_ADDRESS_SIZE(family), state);
}
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state) {
assert(a);
assert(state);
siphash24_compress(&a->family, sizeof(a->family), state);
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
siphash24_compress_typesafe(a->family, state);
in_addr_hash_func(&a->address, a->family, state);
}
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y) {
@ -960,7 +976,7 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
assert(addr);
assert(state);
siphash24_compress(addr, sizeof(*addr), state);
siphash24_compress_typesafe(*addr, state);
}
int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {

View file

@ -144,9 +144,18 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in4_addr_prefix_covers_full(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address, unsigned char address_prefixlen);
int in6_addr_prefix_covers_full(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address, unsigned char address_prefixlen);
int in_addr_prefix_covers_full(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address, unsigned char address_prefixlen);
static inline int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address) {
return in4_addr_prefix_covers_full(prefix, prefixlen, address, 32);
}
static inline int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address) {
return in6_addr_prefix_covers_full(prefix, prefixlen, address, 128);
}
static inline int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address) {
return in_addr_prefix_covers_full(family, prefix, prefixlen, address, family == AF_INET ? 32 : family == AF_INET6 ? 128 : 0);
}
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@ -176,6 +185,7 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
* See also oss-fuzz#11344. */
#define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state);
void in_addr_data_hash_func(const struct in_addr_data *a, struct siphash *state);
int in_addr_data_compare_func(const struct in_addr_data *x, const struct in_addr_data *y);
void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
@ -186,6 +196,16 @@ extern const struct hash_ops in_addr_data_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
extern const struct hash_ops in6_addr_hash_ops_free;
static inline void PTR_TO_IN4_ADDR(const void *p, struct in_addr *ret) {
assert(ret);
ret->s_addr = (uint32_t) ((uintptr_t) p);
}
static inline void* IN4_ADDR_TO_PTR(const struct in_addr *a) {
assert(a);
return (void*) ((uintptr_t) a->s_addr);
}
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
#define IPV4_ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \

View file

@ -6,6 +6,43 @@
#include "inotify-util.h"
#include "stat-util.h"
bool inotify_event_next(
union inotify_event_buffer *buffer,
size_t size,
struct inotify_event **iterator,
int log_level) {
struct inotify_event *e;
size_t offset = 0;
assert(buffer);
assert(iterator);
if (*iterator) {
assert((uint8_t*) *iterator >= buffer->raw);
offset = (uint8_t*) *iterator - buffer->raw;
offset += offsetof(struct inotify_event, name) + (*iterator)->len;
}
if (size == offset)
return false; /* reached end of list */
if (size < offset ||
size - offset < offsetof(struct inotify_event, name)) {
log_full(log_level, "Received invalid inotify event, ignoring.");
return false;
}
e = CAST_ALIGN_PTR(struct inotify_event, buffer->raw + offset);
if (size - offset - offsetof(struct inotify_event, name) < e->len) {
log_full(log_level, "Received invalid inotify event, ignoring.");
return false;
}
*iterator = e;
return true;
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
int wd, r;

View file

@ -10,29 +10,27 @@
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \
for (struct inotify_event \
*start = &((buffer).ev), \
*end = (struct inotify_event*) ((uint8_t*) start + (sz)), \
*e = start; \
(size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \
((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \
(log_full(log_level, "Received invalid inotify event, ignoring."), false)); \
e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len))
#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
_FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ))
/* This evaluates arguments multiple times */
#define FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
for (struct inotify_event *e = NULL; \
inotify_event_next(&buffer, sz, &e, log_level); )
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
_FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \
_FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
union inotify_event_buffer {
struct inotify_event ev;
uint8_t raw[INOTIFY_EVENT_MAX];
};
bool inotify_event_next(
union inotify_event_buffer *buffer,
size_t size,
struct inotify_event **iterator,
int log_level);
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);

View file

@ -7,7 +7,9 @@
#include <stdio.h>
#include <unistd.h>
#include "errno-util.h"
#include "io-util.h"
#include "iovec-util.h"
#include "string-util.h"
#include "time-util.h"
@ -58,8 +60,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
assert(fd >= 0);
/* If called with nbytes == 0, let's call read() at least
* once, to validate the operation */
/* If called with nbytes == 0, let's call read() at least once, to validate the operation */
if (nbytes > (size_t) SSIZE_MAX)
return -EINVAL;
@ -111,13 +112,29 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
}
#if 0 /* NM_IGNORED */
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = ASSERT_PTR(buf);
int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) {
const uint8_t *p;
usec_t end;
int r;
assert(fd >= 0);
assert(buf || nbytes == 0);
if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
return -EINVAL;
if (nbytes == 0) {
static const dummy_t dummy[0];
assert_cc(sizeof(dummy) == 0);
p = (const void*) dummy; /* Some valid pointer, in case NULL was specified */
} else {
if (nbytes == SIZE_MAX)
nbytes = strlen(buf);
else if (_unlikely_(nbytes > (size_t) SSIZE_MAX))
return -EINVAL;
p = buf;
}
/* When timeout is 0 or USEC_INFINITY this is not used. But we initialize it to a sensible value. */
end = timestamp_is_set(timeout) ? usec_add(now(CLOCK_MONOTONIC), timeout) : USEC_INFINITY;
do {
ssize_t k;
@ -127,16 +144,31 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
if (errno == EINTR)
continue;
if (errno == EAGAIN && do_poll) {
/* We knowingly ignore any return value here,
* and expect that any error/EOF is reported
* via write() */
if (errno != EAGAIN || timeout == 0)
return -errno;
(void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY);
continue;
usec_t wait_for;
if (timeout == USEC_INFINITY)
wait_for = USEC_INFINITY;
else {
usec_t t = now(CLOCK_MONOTONIC);
if (t >= end)
return -ETIME;
wait_for = usec_sub_unsigned(end, t);
}
return -errno;
r = fd_wait_for_event(fd, POLLOUT, wait_for);
if (timeout == USEC_INFINITY || ERRNO_IS_NEG_TRANSIENT(r))
/* If timeout == USEC_INFINITY we knowingly ignore any return value
* here, and expect that any error/EOF is reported via write() */
continue;
if (r < 0)
return r;
if (r == 0)
return -ETIME;
continue;
}
if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */
@ -260,7 +292,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return -EIO;
}
if (lseek(fd, n, SEEK_CUR) == (off_t) -1)
if (lseek(fd, n, SEEK_CUR) < 0)
return -errno;
q += n;
@ -281,102 +313,4 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
return q - (const uint8_t*) p;
}
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
char *x;
x = strjoin(field, value);
if (x)
iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
return x;
}
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
char *x;
x = set_iovec_string_field(iovec, n_iovec, field, value);
free(value);
return x;
}
struct iovec_wrapper *iovw_new(void) {
return malloc0(sizeof(struct iovec_wrapper));
}
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
if (free_vectors)
for (size_t i = 0; i < iovw->count; i++)
free(iovw->iovec[i].iov_base);
iovw->iovec = mfree(iovw->iovec);
iovw->count = 0;
}
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
iovw_free_contents(iovw, true);
return mfree(iovw);
}
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
iovw_free_contents(iovw, false);
return mfree(iovw);
}
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
if (iovw->count >= IOV_MAX)
return -E2BIG;
if (!GREEDY_REALLOC(iovw->iovec, iovw->count + 1))
return -ENOMEM;
iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
return 0;
}
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
_cleanup_free_ char *x = NULL;
int r;
x = strjoin(field, value);
if (!x)
return -ENOMEM;
r = iovw_put(iovw, x, strlen(x));
if (r >= 0)
TAKE_PTR(x);
return r;
}
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
_cleanup_free_ _unused_ char *free_ptr = value;
return iovw_put_string_field(iovw, field, value);
}
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
for (size_t i = 0; i < iovw->count; i++)
iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
}
size_t iovw_size(struct iovec_wrapper *iovw) {
size_t n = 0;
for (size_t i = 0; i < iovw->count; i++)
n += iovw->iovec[i].iov_len;
return n;
}
void iovec_array_free(struct iovec *iov, size_t n) {
if (!iov)
return;
for (size_t i = 0; i < n; i++)
free(iov[i].iov_base);
free(iov);
}
#endif /* NM_IGNORED */

View file

@ -6,7 +6,6 @@
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "macro.h"
#include "time-util.h"
@ -15,7 +14,11 @@ int flush_fd(int fd);
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
static inline int loop_write(int fd, const void *buf, size_t nbytes) {
return loop_write_full(fd, buf, nbytes, 0);
}
int pipe_eof(int fd);
@ -24,38 +27,6 @@ int fd_wait_for_event(int fd, int event, usec_t timeout);
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, size_t n) {
size_t r = 0;
for (size_t j = 0; j < n; j++)
r += i[j].iov_len;
return r;
}
static inline bool IOVEC_INCREMENT(struct iovec *i, size_t n, size_t k) {
/* Returns true if there is nothing else to send (bytes written cover all of the iovec),
* false if there's still work to do. */
for (size_t j = 0; j < n; j++) {
size_t sub;
if (i[j].iov_len == 0)
continue;
if (k == 0)
return false;
sub = MIN(i[j].iov_len, k);
i[j].iov_len -= sub;
i[j].iov_base = (uint8_t*) i[j].iov_base + sub;
k -= sub;
}
assert(k == 0); /* Anything else would mean that we wrote more bytes than available,
* or the kernel reported writing more bytes than sent. */
return true;
}
static inline bool FILE_SIZE_VALID(uint64_t l) {
/* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than
* 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */
@ -73,40 +44,3 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
return FILE_SIZE_VALID(l);
}
#define IOVEC_NULL (struct iovec) {}
#define IOVEC_MAKE(base, len) (struct iovec) { .iov_base = (base), .iov_len = (len) }
#define IOVEC_MAKE_STRING(string) \
({ \
char *_s = (char*) (string); \
IOVEC_MAKE(_s, strlen(_s)); \
})
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
struct iovec_wrapper {
struct iovec *iovec;
size_t count;
};
struct iovec_wrapper *iovw_new(void);
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
/* Move data into iovw or free on error */
int r = iovw_put(iovw, data, len);
if (r < 0)
free(data);
return r;
}
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
size_t iovw_size(struct iovec_wrapper *iovw);
void iovec_array_free(struct iovec *iov, size_t n);

View file

@ -0,0 +1,99 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#include <sys/uio.h>
#include "alloc-util.h"
#include "macro.h"
/* An iovec pointing to a single NUL byte */
#define IOVEC_NUL_BYTE (const struct iovec) { \
.iov_base = (void*) (const uint8_t[1]) { 0 }, \
.iov_len = 1, \
}
size_t iovec_total_size(const struct iovec *iovec, size_t n);
bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
/* This accepts both const and non-const pointers */
#define IOVEC_MAKE(base, len) \
(struct iovec) { \
.iov_base = (void*) (base), \
.iov_len = (len), \
}
static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
assert(iovec);
/* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */
*iovec = IOVEC_MAKE(s, s ? strlen(s) : 0);
return iovec;
}
#define IOVEC_MAKE_STRING(s) \
*iovec_make_string(&(struct iovec) {}, s)
#define CONST_IOVEC_MAKE_STRING(s) \
(const struct iovec) { \
.iov_base = (char*) s, \
.iov_len = STRLEN(s), \
}
static inline void iovec_done(struct iovec *iovec) {
/* A _cleanup_() helper that frees the iov_base in the iovec */
assert(iovec);
iovec->iov_base = mfree(iovec->iov_base);
iovec->iov_len = 0;
}
static inline void iovec_done_erase(struct iovec *iovec) {
assert(iovec);
iovec->iov_base = erase_and_free(iovec->iov_base);
iovec->iov_len = 0;
}
static inline bool iovec_is_set(const struct iovec *iovec) {
/* Checks if the iovec points to a non-empty chunk of memory */
return iovec && iovec->iov_len > 0 && iovec->iov_base;
}
static inline bool iovec_is_valid(const struct iovec *iovec) {
/* Checks if the iovec is either NULL, empty or points to a valid bit of memory */
return !iovec || (iovec->iov_base || iovec->iov_len == 0);
}
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
void iovec_array_free(struct iovec *iovec, size_t n_iovec);
static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
if (a == b)
return 0;
return memcmp_nn(a ? a->iov_base : NULL,
a ? a->iov_len : 0,
b ? b->iov_base : NULL,
b ? b->iov_len : 0);
}
static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
assert(ret);
if (!iovec_is_set(source))
*ret = (struct iovec) {};
else {
void *p = memdup(source->iov_base, source->iov_len);
if (!p)
return NULL;
*ret = IOVEC_MAKE(p, source->iov_len);
}
return ret;
}

View file

@ -192,6 +192,18 @@
_p; \
})
#define LIST_CLEAR(name, head, free_func) \
_LIST_CLEAR(name, head, free_func, UNIQ_T(elem, UNIQ))
/* Clear the list, destroying each element with free_func */
#define _LIST_CLEAR(name, head, free_func, elem) \
({ \
typeof(head) elem; \
while ((elem = LIST_POP(name, head))) \
free_func(elem); \
head; \
})
/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
* it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
#include "macro.h"

View file

@ -19,6 +19,7 @@
#include "fileio.h"
#include "hashmap.h"
#include "locale-util.h"
#include "missing_syscall.h"
#include "path-util.h"
#include "set.h"
#include "string-table.h"
@ -223,7 +224,7 @@ int get_locales(char ***ret) {
locales = set_free(locales);
r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
if (r == -ENXIO || r == 0) {
if (IN_SET(r, -ENXIO, 0)) {
char **a, **b;
/* Filter out non-UTF-8 locales, because it's 2019, by default */
@ -282,11 +283,6 @@ int locale_is_installed(const char *name) {
return true;
}
void init_gettext(void) {
setlocale(LC_ALL, "");
textdomain(GETTEXT_PACKAGE);
}
#endif /* NM_IGNORED */
bool is_locale_utf8(void) {
@ -307,6 +303,12 @@ bool is_locale_utf8(void) {
} else if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
/* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
if (gettid() != raw_getpid()) {
cached_answer = true;
goto out;
}
if (!setlocale(LC_ALL, "")) {
cached_answer = true;
goto out;
@ -345,11 +347,7 @@ out:
#if 0 /* NM_IGNORED */
void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
if (!l)
return;
for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
l[i] = mfree(l[i]);
free_many_charp(l, _VARIABLE_LC_MAX);
}
void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {

View file

@ -33,9 +33,8 @@ int get_locales(char ***l);
bool locale_is_valid(const char *name);
int locale_is_installed(const char *name);
#define _(String) gettext(String)
#define _(String) dgettext(GETTEXT_PACKAGE, String)
#define N_(String) String
void init_gettext(void);
bool is_locale_utf8(void);

View file

@ -34,9 +34,12 @@ void unposix_unlockpp(int **fd);
_cleanup_(unposix_unlockpp) _unused_ int *CONCATENATE(_cleanup_unposix_unlock_, UNIQ) = &(fd)
typedef enum LockType {
LOCK_NONE, /* Don't lock the file descriptor. Useful if you need to conditionally lock a file. */
LOCK_BSD,
LOCK_POSIX,
LOCK_UNPOSIX,
} LockType;
int lock_generic(int fd, LockType type, int operation);
int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout);

View file

@ -467,6 +467,9 @@ void log_set_open_when_needed(bool b);
* stderr, the console or kmsg */
void log_set_prohibit_ipc(bool b);
void log_set_assert_return_is_critical(bool b);
bool log_get_assert_return_is_critical(void) _pure_;
int log_dup_console(void);
#if 0 /* NM_IGNORED */

View file

@ -14,7 +14,7 @@
/* Note: on GCC "no_sanitize_address" is a function attribute only, on llvm it may also be applied to global
* variables. We define a specific macro which knows this. Note that on GCC we don't need this decorator so much, since
* our primary usecase for this attribute is registration structures placed in named ELF sections which shall not be
* our primary use case for this attribute is registration structures placed in named ELF sections which shall not be
* padded, but GCC doesn't pad those anyway if AddressSanitizer is enabled. */
#if HAS_FEATURE_ADDRESS_SANITIZER && defined(__clang__)
#define _variable_no_sanitize_address_ __attribute__((__no_sanitize_address__))
@ -84,7 +84,7 @@
#define REENABLE_WARNING
#endif
/* automake test harness */
/* test harness */
#define EXIT_TEST_SKIP 77
/* builtins */
@ -96,6 +96,13 @@
#error "neither int nor long are four bytes long?!?"
#endif
static inline uint64_t u64_multiply_safe(uint64_t a, uint64_t b) {
if (_unlikely_(a != 0 && b > (UINT64_MAX / a)))
return 0; /* overflow */
return a * b;
}
/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
static inline unsigned long ALIGN_POWER2(unsigned long u) {
@ -198,7 +205,7 @@ static inline int __coverity_check_and_return__(int condition) {
/* We override the glibc assert() here. */
#undef assert
#ifdef NDEBUG
#define assert(expr) do {} while (false)
#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
#else
#define assert(expr) assert_message_se(expr, #expr)
#endif
@ -304,12 +311,6 @@ static inline int __coverity_check_and_return__(int condition) {
/* Pointers range from NULL to POINTER_MAX */
#define POINTER_MAX ((void*) UINTPTR_MAX)
/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses POINTER_MAX as internal marker for EOL. */
#define FOREACH_POINTER(p, x, ...) \
for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, POINTER_MAX }; \
p != (typeof(p)) POINTER_MAX; \
p = *(++_l))
#define _FOREACH_ARRAY(i, array, num, m, end) \
for (typeof(array[0]) *i = (array), *end = ({ \
typeof(num) m = (num); \
@ -319,31 +320,6 @@ static inline int __coverity_check_and_return__(int condition) {
#define FOREACH_ARRAY(i, array, num) \
_FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ))
#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func) \
static inline void name(type *p) { \
func(p); \
}
/* When func() returns the void value (NULL, -1, …) of the appropriate type */
#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
static inline void func##p(type *p) { \
if (*p) \
*p = func(*p); \
}
/* When func() doesn't return the appropriate type, set variable to empty afterwards.
* The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty) \
static inline void func##p(type *p) { \
if (*p != (empty)) { \
DISABLE_WARNING_ADDRESS; \
assert(func); \
REENABLE_WARNING; \
func(*p); \
*p = (empty); \
} \
}
#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \
scope type *name##_ref(type *p) { \
if (!p) \
@ -443,13 +419,13 @@ assert_cc(sizeof(dummy_t) == 0);
_q && _q > (base) ? &_q[-1] : NULL; \
})
/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly
/* Iterate through each argument passed. All must be the same type as 'entry' or must be implicitly
* convertible. The iteration variable 'entry' must already be defined. */
#define VA_ARGS_FOREACH(entry, ...) \
_VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), ##__VA_ARGS__)
#define _VA_ARGS_FOREACH(entry, _entries_, _current_, ...) \
for (typeof(entry) _entries_[] = { __VA_ARGS__ }, *_current_ = _entries_; \
((long)(_current_ - _entries_) < (long)ELEMENTSOF(_entries_)) && ({ entry = *_current_; true; }); \
#define FOREACH_ARGUMENT(entry, ...) \
_FOREACH_ARGUMENT(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), UNIQ_T(_va_sentinel_, UNIQ), ##__VA_ARGS__)
#define _FOREACH_ARGUMENT(entry, _entries_, _current_, _va_sentinel_, ...) \
for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \
((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
_current_++)
#include "log.h"

View file

@ -41,3 +41,19 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
void *memdup_reverse(const void *mem, size_t size) {
assert(mem);
assert(size != 0);
void *p = malloc(size);
if (!p)
return NULL;
uint8_t *p_dst = p;
const uint8_t *p_src = mem;
for (size_t i = 0, k = size; i < size; i++, k--)
p_dst[i] = p_src[k-1];
return p;
}

View file

@ -12,9 +12,12 @@
#include "memory-util-fundamental.h"
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
#define PAGE_ALIGN(l) ALIGN_TO(l, page_size())
#define PAGE_ALIGN_U64(l) ALIGN_TO_U64(l, page_size())
#define PAGE_ALIGN_DOWN(l) ALIGN_DOWN(l, page_size())
#define PAGE_ALIGN_DOWN_U64(l) ALIGN_DOWN_U64(l, page_size())
#define PAGE_OFFSET(l) ALIGN_OFFSET(l, page_size())
#define PAGE_OFFSET_U64(l) ALIGN_OFFSET_U64(l, page_size())
/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */
static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
@ -47,13 +50,6 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
?: CMP(n1, n2);
}
#define memzero(x,l) \
({ \
size_t _l_ = (l); \
if (_l_ > 0) \
memset(x, 0, _l_); \
})
#define zero(x) (memzero(&(x), sizeof(x)))
bool memeqbyte(uint8_t byte, const void *data, size_t length);
@ -112,36 +108,5 @@ static inline void erase_char(char *p) {
explicit_bzero_safe(p, sizeof(char));
}
/* An automatic _cleanup_-like logic for destroy arrays (i.e. pointers + size) when leaving scope */
typedef struct ArrayCleanup {
void **parray;
size_t *pn;
free_array_func_t pfunc;
} ArrayCleanup;
static inline void array_cleanup(const ArrayCleanup *c) {
assert(c);
assert(!c->parray == !c->pn);
if (!c->parray)
return;
if (*c->parray) {
assert(c->pfunc);
c->pfunc(*c->parray, *c->pn);
*c->parray = NULL;
}
*c->pn = 0;
}
#define CLEANUP_ARRAY(array, n, func) \
_cleanup_(array_cleanup) _unused_ const ArrayCleanup CONCATENATE(_cleanup_array_, UNIQ) = { \
.parray = (void**) &(array), \
.pn = &(n), \
.pfunc = (free_array_func_t) ({ \
void (*_f)(typeof(array[0]) *a, size_t b) = func; \
_f; \
}), \
}
/* Makes a copy of the buffer with reversed order of bytes */
void *memdup_reverse(const void *mem, size_t size);

View file

@ -7,7 +7,6 @@
#if HAVE_LINUX_VM_SOCKETS_H
#include <linux/vm_sockets.h>
#else
#define VMADDR_CID_ANY -1U
struct sockaddr_vm {
unsigned short svm_family;
unsigned short svm_reserved1;
@ -22,6 +21,26 @@ struct sockaddr_vm {
#endif /* !HAVE_LINUX_VM_SOCKETS_H */
#endif /* NM_IGNORED */
#ifndef VMADDR_CID_ANY
#define VMADDR_CID_ANY -1U
#endif
#ifndef VMADDR_CID_HYPERVISOR
#define VMADDR_CID_HYPERVISOR 0U
#endif
#ifndef VMADDR_CID_LOCAL
#define VMADDR_CID_LOCAL 1U
#endif
#ifndef VMADDR_CID_HOST
#define VMADDR_CID_HOST 2U
#endif
#ifndef VMADDR_PORT_ANY
#define VMADDR_PORT_ANY -1U
#endif
#ifndef AF_VSOCK
#define AF_VSOCK 40
#endif
@ -34,6 +53,10 @@ struct sockaddr_vm {
#define SO_PEERGROUPS 59
#endif
#ifndef SO_PEERPIDFD
#define SO_PEERPIDFD 77
#endif
#ifndef SO_BINDTOIFINDEX
#define SO_BINDTOIFINDEX 62
#endif

View file

@ -9,7 +9,7 @@
#include <linux/stat.h>
#endif
/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
#define STATX_DEFINITION { \
__u32 stx_mask; \
__u32 stx_blksize; \

View file

@ -34,6 +34,21 @@
/* ======================================================================= */
#if !HAVE_FCHMODAT2
static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
# ifdef __NR_fchmodat2
return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
# else
errno = ENOSYS;
return -1;
# endif
}
# define fchmodat2 missing_fchmodat2
#endif
/* ======================================================================= */
#if !HAVE_PIVOT_ROOT
static inline int missing_pivot_root(const char *new_root, const char *put_old) {
return syscall(__NR_pivot_root, new_root, put_old);
@ -546,6 +561,10 @@ static inline int missing_open_tree(
/* ======================================================================= */
#ifndef MOVE_MOUNT_BENEATH
#define MOVE_MOUNT_BENEATH 0x00000200
#endif
#if !HAVE_MOVE_MOUNT
#ifndef MOVE_MOUNT_F_EMPTY_PATH
@ -662,3 +681,17 @@ static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
# define getdents64 missing_getdents64
#endif
#endif /* NM_IGNORED */
/* ======================================================================= */
/* glibc does not provide clone() on ia64, only clone2(). Not only that, but it also doesn't provide a
* prototype, only the symbol in the shared library (it provides a prototype for clone(), but not the
* symbol in the shared library). */
#if defined(__ia64__)
int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags, void *arg);
#define HAVE_CLONE 0
#else
/* We know that everywhere else clone() is available, so we don't bother with a meson check (that takes time
* at build time) and just define it. Once the kernel drops ia64 support, we can drop this too. */
#define HAVE_CLONE 1
#endif

View file

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
typedef enum NamespaceType {
NAMESPACE_CGROUP,
NAMESPACE_IPC,
NAMESPACE_NET,
NAMESPACE_MOUNT,
NAMESPACE_PID,
NAMESPACE_USER,
NAMESPACE_UTS,
NAMESPACE_TIME,
_NAMESPACE_TYPE_MAX,
_NAMESPACE_TYPE_INVALID = -EINVAL,
} NamespaceType;
extern const struct namespace_info {
const char *proc_name;
const char *proc_path;
unsigned int clone_flag;
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
int namespace_open(
pid_t pid,
int *ret_pidns_fd,
int *ret_mntns_fd,
int *ret_netns_fd,
int *ret_userns_fd,
int *ret_root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
int fd_is_ns(int fd, unsigned long nsflag);
int detach_mount_namespace(void);
static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
/* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end
* doesn't overflow uid_t. */
assert_cc((uid_t) -1 > 0); /* verify that uid_t is unsigned */
if (range <= 0)
return false;
if (shift > (uid_t) -1 - range)
return false;
return true;
}
int userns_acquire(const char *uid_map, const char *gid_map);
int netns_acquire(void);
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);

View file

@ -93,13 +93,16 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s) {
bool space = false;
char *p;
assert(f);
assert(field);
if (ordered_set_isempty(s))
return;
fputs(field, f);
ORDERED_SET_FOREACH(p, s)
fputs_with_space(f, p, NULL, &space);
fputs_with_separator(f, p, NULL, &space);
fputc('\n', f);
}

View file

@ -47,6 +47,24 @@ int parse_boolean(const char *v) {
}
#if 0 /* NM_IGNORED */
int parse_tristate_full(const char *v, const char *third, int *ret) {
int r;
if (isempty(v) || streq_ptr(v, third)) { /* Empty string is always taken as the third/invalid/auto state */
if (ret)
*ret = -1;
} else {
r = parse_boolean(v);
if (r < 0)
return r;
if (ret)
*ret = r;
}
return 0;
}
int parse_pid(const char *s, pid_t* ret_pid) {
unsigned long ul = 0;
pid_t pid;
@ -110,8 +128,7 @@ int parse_ifindex(const char *s) {
#if 0 /* NM_IGNORED */
int parse_mtu(int family, const char *s, uint32_t *ret) {
uint64_t u;
size_t m;
uint64_t u, m;
int r;
r = parse_size(s, 1024, &u);
@ -121,10 +138,16 @@ int parse_mtu(int family, const char *s, uint32_t *ret) {
if (u > UINT32_MAX)
return -ERANGE;
if (family == AF_INET6)
switch (family) {
case AF_INET:
m = IPV4_MIN_MTU; /* This is 68 */
break;
case AF_INET6:
m = IPV6_MIN_MTU; /* This is 1280 */
else
m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */
break;
default:
m = 0;
}
if (u < m)
return -ERANGE;
@ -431,6 +454,21 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
return 0;
}
int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) {
unsigned v;
int r;
r = safe_atou(s, &v);
if (r < 0)
return r;
if (v < min || v > max)
return -ERANGE;
*ret = v;
return 0;
}
int safe_atoi(const char *s, int *ret_i) {
unsigned base = 0;
char *x = NULL;
@ -660,7 +698,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
return 0;
}
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
unsigned l, h;
int r;
@ -668,7 +706,10 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) {
if (r < 0)
return r;
if (l <= 0 || l > 65535 || h <= 0 || h > 65535)
if (l > 65535 || h > 65535)
return -EINVAL;
if (!allow_zero && (l == 0 || h == 0))
return -EINVAL;
if (h < l)
@ -754,4 +795,23 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
return store_loadavg_fixed_point(i, f, ret);
}
/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
* https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
bool nft_identifier_valid(const char *id) {
if (!id)
return false;
size_t len = strlen(id);
if (len == 0 || len > 31)
return false;
if (!ascii_isalpha(id[0]))
return false;
for (size_t i = 1; i < len; i++)
if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
return false;
return true;
}
#endif /* NM_IGNORED */

View file

@ -12,6 +12,10 @@
typedef unsigned long loadavg_t;
int parse_boolean(const char *v) _pure_;
int parse_tristate_full(const char *v, const char *third, int *ret);
static inline int parse_tristate(const char *v, int *ret) {
return parse_tristate_full(v, NULL, ret);
}
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s);
@ -30,11 +34,12 @@ int parse_fd(const char *t);
#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS)
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
static inline int safe_atou(const char *s, unsigned *ret_u) {
return safe_atou_full(s, 0, ret_u);
}
int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
int safe_atoi(const char *s, int *ret_i);
int safe_atolli(const char *s, long long int *ret_i);
@ -134,7 +139,7 @@ int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
int parse_nice(const char *p, int *ret);
int parse_ip_port(const char *s, uint16_t *ret);
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high);
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero);
int parse_ip_prefix_length(const char *s, int *ret);
@ -152,3 +157,5 @@ int parse_oom_score_adjust(const char *s, int *ret);
* to a loadavg_t. */
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret);
int parse_loadavg_fixed_point(const char *s, loadavg_t *ret);
bool nft_identifier_valid(const char *id);

View file

@ -43,7 +43,7 @@ int path_split_and_make_absolute(const char *p, char ***ret) {
return r;
}
char *path_make_absolute(const char *p, const char *prefix) {
char* path_make_absolute(const char *p, const char *prefix) {
assert(p);
/* Makes every item in the list an absolute path by prepending
@ -135,11 +135,9 @@ int path_make_relative(const char *from, const char *to, char **ret) {
return -ENOMEM;
} else {
/* 'to' is inside of 'from'. */
result = strdup(t);
if (!result)
return -ENOMEM;
path_simplify(result);
r = path_simplify_alloc(t, &result);
if (r < 0)
return r;
if (!path_is_valid(result))
return -EINVAL;
@ -255,7 +253,7 @@ int path_strv_make_absolute_cwd(char **l) {
return 0;
}
char **path_strv_resolve(char **l, const char *root) {
char** path_strv_resolve(char **l, const char *root) {
unsigned k = 0;
bool enomem = false;
int r;
@ -336,7 +334,7 @@ char **path_strv_resolve(char **l, const char *root) {
return l;
}
char **path_strv_resolve_uniq(char **l, const char *root) {
char** path_strv_resolve_uniq(char **l, const char *root) {
if (strv_isempty(l))
return l;
@ -348,9 +346,9 @@ char **path_strv_resolve_uniq(char **l, const char *root) {
}
#endif /* NM_IGNORED */
char *path_simplify(char *path) {
bool add_slash = false;
char *f = ASSERT_PTR(path);
char* path_simplify_full(char *path, PathSimplifyFlags flags) {
bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
char *f = path;
int r;
/* Removes redundant inner and trailing slashes. Also removes unnecessary dots.
@ -358,13 +356,17 @@ char *path_simplify(char *path) {
*
* ///foo//./bar/. becomes /foo/bar
* .//./foo//./bar/. becomes foo/bar
* /../foo/bar becomes /foo/bar
* /../foo/bar/.. becomes /foo/bar/..
*/
if (isempty(path))
return path;
if (path_is_absolute(path))
f++;
keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
absolute = path_is_absolute(path);
f += absolute; /* Keep leading /, if present. */
for (const char *p = f;;) {
const char *e;
@ -373,11 +375,17 @@ char *path_simplify(char *path) {
if (r == 0)
break;
if (r > 0 && absolute && beginning && path_startswith(e, ".."))
/* If we're at the beginning of an absolute path, we can safely skip ".." */
continue;
beginning = false;
if (add_slash)
*f++ = '/';
if (r < 0) {
/* if path is invalid, then refuse to simplify remaining part. */
/* if path is invalid, then refuse to simplify the remaining part. */
memmove(f, p, strlen(p) + 1);
return path;
}
@ -392,11 +400,14 @@ char *path_simplify(char *path) {
if (f == path)
*f++ = '.';
if (*(f-1) != '/' && keep_trailing_slash)
*f++ = '/';
*f = '\0';
return path;
}
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
assert(path);
assert(prefix);
@ -489,10 +500,6 @@ int path_compare(const char *a, const char *b) {
}
}
bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
return path_equal(a, b) || inode_same(a, b, flags) > 0;
}
int path_compare_filename(const char *a, const char *b) {
_cleanup_free_ char *fa = NULL, *fb = NULL;
int r, j, k;
@ -661,7 +668,14 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
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 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;
@ -812,7 +826,7 @@ int fsck_exists_for_fstype(const char *fstype) {
}
#endif /* NM_IGNORED */
static const char *skip_slash_or_dot(const char *p) {
static const char* skip_slash_or_dot(const char *p) {
for (; !isempty(p); p++) {
if (*p == '/')
continue;
@ -896,7 +910,7 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char **
return len;
}
static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
static const char* skip_slash_or_dot_backward(const char *path, const char *q) {
assert(path);
assert(!q || q >= path);
@ -1005,7 +1019,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
return len;
}
const char *last_path_component(const char *path) {
const char* last_path_component(const char *path) {
/* Finds the last component of the path, preserving the optional trailing slash that signifies a directory.
*
@ -1126,17 +1140,19 @@ int path_extract_directory(const char *path, char **ret) {
if (!path_is_valid(a))
return -EINVAL;
*ret = TAKE_PTR(a);
if (ret)
*ret = TAKE_PTR(a);
return 0;
}
bool filename_is_valid(const char *p) {
bool filename_part_is_valid(const char *p) {
const char *e;
if (isempty(p))
return false;
/* Checks f the specified string is OK to be *part* of a filename. This is different from
* filename_is_valid() as "." and ".." and "" are OK by this call, but not by filename_is_valid(). */
if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
if (!p)
return false;
e = strchrnul(p, '/');
@ -1149,6 +1165,17 @@ bool filename_is_valid(const char *p) {
return true;
}
bool filename_is_valid(const char *p) {
if (isempty(p))
return false;
if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
return false;
return filename_part_is_valid(p);
}
bool path_is_valid_full(const char *p, bool accept_dot_dot) {
if (isempty(p))
return false;
@ -1265,9 +1292,16 @@ bool hidden_or_backup_file(const char *filename) {
bool is_device_path(const char *path) {
/* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
* /dev. */
* /dev. This accepts any path that starts with /dev/ or /sys/ and has something after that prefix.
* It does not actually resolve the path.
*
* Examples:
* /dev/sda, /dev/sda/foo, /sys/class, /dev/.., /sys/.., /./dev/foo yes.
* /../dev/sda, /dev, /sys, /usr/path, /usr/../dev/sda no.
*/
return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
const char *p = PATH_STARTSWITH_SET(ASSERT_PTR(path), "/dev/", "/sys/");
return !isempty(p);
}
bool valid_device_node_path(const char *path) {

View file

@ -6,6 +6,7 @@
#include <stddef.h>
#include "macro.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@ -25,24 +26,14 @@
# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
#endif
#define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
#define DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/")
#define DEFAULT_PATH_SPLIT_USR_NULSTR DEFAULT_PATH_NORMAL_NULSTR PATH_SBIN_BIN_NULSTR("/")
#define DEFAULT_PATH PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
#define DEFAULT_PATH_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/")
#if HAVE_SPLIT_USR
# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR
# define DEFAULT_PATH_NULSTR DEFAULT_PATH_SPLIT_USR_NULSTR
#else
# define DEFAULT_PATH DEFAULT_PATH_NORMAL
# define DEFAULT_PATH_NULSTR DEFAULT_PATH_NORMAL_NULSTR
#endif
#endif /* NM_IGNORED */
#ifndef DEFAULT_USER_PATH
# define DEFAULT_USER_PATH DEFAULT_PATH
#endif
#endif /* NM_IGNORED */
static inline bool is_path(const char *p) {
if (!p) /* A NULL pointer is definitely not a path */
@ -64,7 +55,7 @@ int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from, const char *to, char **ret);
int path_make_relative_parent(const char *from_child, const char *to, char **ret);
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
static inline char* path_startswith(const char *path, const char *prefix) {
return path_startswith_full(path, prefix, true);
}
@ -79,13 +70,38 @@ static inline bool path_equal_filename(const char *a, const char *b) {
return path_compare_filename(a, b) == 0;
}
bool path_equal_or_inode_same(const char *a, const char *b, int flags);
static inline bool path_equal_or_inode_same(const char *a, const char *b, int flags) {
return path_equal(a, b) || inode_same(a, b, flags) > 0;
}
char* path_extend_internal(char **x, ...);
#define path_extend(x, ...) path_extend_internal(x, __VA_ARGS__, POINTER_MAX)
#define path_join(...) path_extend_internal(NULL, __VA_ARGS__, POINTER_MAX)
char* path_simplify(char *path);
typedef enum PathSimplifyFlags {
PATH_SIMPLIFY_KEEP_TRAILING_SLASH = 1 << 0,
} PathSimplifyFlags;
char* path_simplify_full(char *path, PathSimplifyFlags flags);
static inline char* path_simplify(char *path) {
return path_simplify_full(path, 0);
}
static inline int path_simplify_alloc(const char *path, char **ret) {
assert(ret);
if (!path) {
*ret = NULL;
return 0;
}
char *t = strdup(path);
if (!t)
return -ENOMEM;
*ret = path_simplify(t);
return 0;
}
static inline bool path_equal_ptr(const char *a, const char *b) {
return !!a == !!b && (!a || path_equal(a, b));
@ -142,7 +158,7 @@ int fsck_exists_for_fstype(const char *fstype);
char *_p, *_n; \
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
_path ++; \
_path++; \
if (isempty(_root)) \
_ret = _path; \
else { \
@ -161,10 +177,11 @@ int fsck_exists_for_fstype(const char *fstype);
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
const char *last_path_component(const char *path);
const char* last_path_component(const char *path);
int path_extract_filename(const char *path, char **ret);
int path_extract_directory(const char *path, char **ret);
bool filename_part_is_valid(const char *p) _pure_;
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
static inline bool path_is_valid(const char *p) {
@ -197,7 +214,7 @@ static inline const char *skip_dev_prefix(const char *p) {
}
bool empty_or_root(const char *path);
static inline const char *empty_to_root(const char *path) {
static inline const char* empty_to_root(const char *path) {
return isempty(path) ? "/" : path;
}

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "macro.h"
/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */
typedef struct PidRef {
pid_t pid; /* always valid */
int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */
} PidRef;
#define PIDREF_NULL (const PidRef) { .fd = -EBADF }
/* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
* pidref_set_pid() which does so *with* acquiring one, see below) */
#define PIDREF_MAKE_FROM_PID(x) (PidRef) { .pid = (x), .fd = -EBADF }
static inline bool pidref_is_set(const PidRef *pidref) {
return pidref && pidref->pid > 0;
}
static inline bool pidref_equal(const PidRef *a, const PidRef *b) {
if (pidref_is_set(a)) {
if (!pidref_is_set(b))
return false;
return a->pid == b->pid;
}
return !pidref_is_set(b);
}
/* This turns a pid_t into a PidRef structure, and acquires a pidfd for it, if possible. (As opposed to
* PIDREF_MAKE_FROM_PID() above, which does not acquire a pidfd.) */
int pidref_set_pid(PidRef *pidref, pid_t pid);
int pidref_set_pidstr(PidRef *pidref, const char *pid);
int pidref_set_pidfd(PidRef *pidref, int fd);
int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
int pidref_set_parent(PidRef *ret);
static inline int pidref_set_self(PidRef *pidref) {
return pidref_set_pid(pidref, 0);
}
bool pidref_is_self(const PidRef *pidref);
void pidref_done(PidRef *pidref);
PidRef *pidref_free(PidRef *pidref);
DEFINE_TRIVIAL_CLEANUP_FUNC(PidRef*, pidref_free);
int pidref_copy(const PidRef *pidref, PidRef *dest);
int pidref_dup(const PidRef *pidref, PidRef **ret);
int pidref_new_from_pid(pid_t pid, PidRef **ret);
int pidref_kill(const PidRef *pidref, int sig);
int pidref_kill_and_sigcont(const PidRef *pidref, int sig);
int pidref_sigqueue(const PidRef *pidref, int sig, int value);
int pidref_wait(const PidRef *pidref, siginfo_t *siginfo, int options);
int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret);
static inline void pidref_done_sigkill_wait(PidRef *pidref) {
if (!pidref_is_set(pidref))
return;
(void) pidref_kill(pidref, SIGKILL);
(void) pidref_wait_for_terminate(pidref, NULL);
pidref_done(pidref);
}
int pidref_verify(const PidRef *pidref);
#define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
extern const struct hash_ops pidref_hash_ops;
extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */

View file

@ -215,7 +215,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
}
}
_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) {
struct prioq_item *i;
assert(q);

View file

@ -7,6 +7,7 @@
#include <limits.h>
#include <linux/oom.h>
#include <pthread.h>
#include <spawn.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -26,6 +27,7 @@
#include "alloc-util.h"
#include "architecture.h"
#include "argv-util.h"
#include "dirent-util.h"
#include "env-file.h"
#include "env-util.h"
#include "errno-util.h"
@ -97,7 +99,7 @@ static int get_process_state(pid_t pid) {
return (unsigned char) state;
}
int get_process_comm(pid_t pid, char **ret) {
int pid_get_comm(pid_t pid, char **ret) {
_cleanup_free_ char *escaped = NULL, *comm = NULL;
int r;
@ -135,15 +137,35 @@ int get_process_comm(pid_t pid, char **ret) {
return 0;
}
static int get_process_cmdline_nulstr(
int pidref_get_comm(const PidRef *pid, char **ret) {
_cleanup_free_ char *comm = NULL;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
r = pid_get_comm(pid->pid, &comm);
if (r < 0)
return r;
r = pidref_verify(pid);
if (r < 0)
return r;
if (ret)
*ret = TAKE_PTR(comm);
return 0;
}
static int pid_get_cmdline_nulstr(
pid_t pid,
size_t max_size,
ProcessCmdlineFlags flags,
char **ret,
size_t *ret_size) {
_cleanup_free_ char *t = NULL;
const char *p;
char *t;
size_t k;
int r;
@ -167,18 +189,17 @@ static int get_process_cmdline_nulstr(
return r;
if (k == 0) {
t = mfree(t);
if (!(flags & PROCESS_CMDLINE_COMM_FALLBACK))
return -ENOENT;
/* Kernel threads have no argv[] */
_cleanup_free_ char *comm = NULL;
r = get_process_comm(pid, &comm);
r = pid_get_comm(pid, &comm);
if (r < 0)
return r;
free(t);
t = strjoin("[", comm, "]");
if (!t)
return -ENOMEM;
@ -189,12 +210,15 @@ static int get_process_cmdline_nulstr(
t[max_size] = '\0';
}
*ret = t;
*ret_size = k;
if (ret)
*ret = TAKE_PTR(t);
if (ret_size)
*ret_size = k;
return r;
}
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
_cleanup_free_ char *t = NULL;
size_t k;
char *ans;
@ -202,7 +226,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
assert(pid >= 0);
assert(ret);
/* Retrieve and format a commandline. See above for discussion of retrieval options.
/* Retrieve and format a command line. See above for discussion of retrieval options.
*
* There are two main formatting modes:
*
@ -216,7 +240,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
* PROCESS_CMDLINE_COMM_FALLBACK is not specified). Returns 0 and sets *line otherwise. */
int full = get_process_cmdline_nulstr(pid, max_columns, flags, &t, &k);
int full = pid_get_cmdline_nulstr(pid, max_columns, flags, &t, &k);
if (full < 0)
return full;
@ -259,7 +283,27 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
return 0;
}
int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
_cleanup_free_ char *s = NULL;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
if (r < 0)
return r;
r = pidref_verify(pid);
if (r < 0)
return r;
if (ret)
*ret = TAKE_PTR(s);
return 0;
}
int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret) {
_cleanup_free_ char *t = NULL;
char **args;
size_t k;
@ -269,7 +313,7 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
assert((flags & ~PROCESS_CMDLINE_COMM_FALLBACK) == 0);
assert(ret);
r = get_process_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
r = pid_get_cmdline_nulstr(pid, SIZE_MAX, flags, &t, &k);
if (r < 0)
return r;
@ -281,6 +325,27 @@ int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret)
return 0;
}
int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret) {
_cleanup_strv_free_ char **args = NULL;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
r = pid_get_cmdline_strv(pid->pid, flags, &args);
if (r < 0)
return r;
r = pidref_verify(pid);
if (r < 0)
return r;
if (ret)
*ret = TAKE_PTR(args);
return 0;
}
int container_get_leader(const char *machine, pid_t *pid) {
_cleanup_free_ char *s = NULL, *class = NULL;
const char *p;
@ -322,7 +387,34 @@ int container_get_leader(const char *machine, pid_t *pid) {
return 0;
}
int is_kernel_thread(pid_t pid) {
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
int r;
assert(ret);
for (;;) {
pid_t ppid;
r = get_process_ppid(pid, &ppid);
if (r < 0)
return r;
r = in_same_namespace(pid, ppid, type);
if (r < 0)
return r;
if (r == 0) {
/* If the parent and the child are not in the same
* namespace, then the child is the leader we are
* looking for. */
*ret = pid;
return 0;
}
pid = ppid;
}
}
int pid_is_kernel_thread(pid_t pid) {
_cleanup_free_ char *line = NULL;
unsigned long long flags;
size_t l, i;
@ -380,6 +472,23 @@ int is_kernel_thread(pid_t pid) {
return !!(flags & PF_KTHREAD);
}
int pidref_is_kernel_thread(const PidRef *pid) {
int result, r;
if (!pidref_is_set(pid))
return -ESRCH;
result = pid_is_kernel_thread(pid->pid);
if (result < 0)
return result;
r = pidref_verify(pid); /* Verify that the PID wasn't reused since */
if (r < 0)
return r;
return result;
}
int get_process_capeff(pid_t pid, char **ret) {
const char *p;
int r;
@ -449,16 +558,14 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
_cleanup_free_ char *line = NULL;
char *l;
r = read_line(f, LONG_LINE_MAX, &line);
r = read_stripped_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0)
break;
l = strstrip(line);
if (startswith(l, field)) {
l += strlen(field);
l = startswith(line, field);
if (l) {
l += strspn(l, WHITESPACE);
l[strcspn(l, WHITESPACE)] = 0;
@ -470,7 +577,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
return -EIO;
}
int get_process_uid(pid_t pid, uid_t *ret) {
int pid_get_uid(pid_t pid, uid_t *ret) {
assert(ret);
if (pid == 0 || pid == getpid_cached()) {
*ret = getuid();
@ -480,6 +588,26 @@ int get_process_uid(pid_t pid, uid_t *ret) {
return get_process_id(pid, "Uid:", ret);
}
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
uid_t uid;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
r = pid_get_uid(pid->pid, &uid);
if (r < 0)
return r;
r = pidref_verify(pid);
if (r < 0)
return r;
if (ret)
*ret = uid;
return 0;
}
int get_process_gid(pid_t pid, gid_t *ret) {
if (pid == 0 || pid == getpid_cached()) {
@ -606,6 +734,82 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
return 0;
}
int pid_get_start_time(pid_t pid, uint64_t *ret) {
_cleanup_free_ char *line = NULL;
const char *p;
int r;
assert(pid >= 0);
p = procfs_file_alloca(pid, "stat");
r = read_one_line_file(p, &line);
if (r == -ENOENT)
return -ESRCH;
if (r < 0)
return r;
/* Let's skip the pid and comm fields. The latter is enclosed in () but does not escape any () in its
* value, so let's skip over it manually */
p = strrchr(line, ')');
if (!p)
return -EIO;
p++;
unsigned long llu;
if (sscanf(p, " "
"%*c " /* state */
"%*u " /* ppid */
"%*u " /* pgrp */
"%*u " /* session */
"%*u " /* tty_nr */
"%*u " /* tpgid */
"%*u " /* flags */
"%*u " /* minflt */
"%*u " /* cminflt */
"%*u " /* majflt */
"%*u " /* cmajflt */
"%*u " /* utime */
"%*u " /* stime */
"%*u " /* cutime */
"%*u " /* cstime */
"%*i " /* priority */
"%*i " /* nice */
"%*u " /* num_threads */
"%*u " /* itrealvalue */
"%lu ", /* starttime */
&llu) != 1)
return -EIO;
if (ret)
*ret = llu;
return 0;
}
int pidref_get_start_time(const PidRef *pid, uint64_t *ret) {
uint64_t t;
int r;
if (!pidref_is_set(pid))
return -ESRCH;
r = pid_get_start_time(pid->pid, ret ? &t : NULL);
if (r < 0)
return r;
r = pidref_verify(pid);
if (r < 0)
return r;
if (ret)
*ret = t;
return 0;
}
int get_process_umask(pid_t pid, mode_t *ret) {
_cleanup_free_ char *m = NULL;
const char *p;
@ -670,7 +874,7 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
assert(pid > 1);
if (!name) {
r = get_process_comm(pid, &buffer);
r = pid_get_comm(pid, &buffer);
if (r < 0)
log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
else
@ -824,7 +1028,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
char *value = NULL;
const char *path;
size_t l, sum = 0;
size_t sum = 0;
int r;
assert(pid >= 0);
@ -859,9 +1063,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
if (r < 0)
return r;
l = strlen(field);
for (;;) {
_cleanup_free_ char *line = NULL;
const char *match;
if (sum > ENVIRONMENT_BLOCK_MAX) /* Give up searching eventually */
return -ENOBUFS;
@ -874,8 +1078,9 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
sum += r;
if (strneq(line, field, l) && line[l] == '=') {
value = strdup(line + l + 1);
match = startswith(line, field);
if (match && *match == '=') {
value = strdup(match + 1);
if (!value)
return -ENOMEM;
@ -892,6 +1097,9 @@ int pid_is_my_child(pid_t pid) {
pid_t ppid;
int r;
if (pid < 0)
return -ESRCH;
if (pid <= 1)
return false;
@ -902,11 +1110,28 @@ int pid_is_my_child(pid_t pid) {
return ppid == getpid_cached();
}
bool pid_is_unwaited(pid_t pid) {
int pidref_is_my_child(const PidRef *pid) {
int r, result;
if (!pidref_is_set(pid))
return -ESRCH;
result = pid_is_my_child(pid->pid);
if (result < 0)
return result;
r = pidref_verify(pid);
if (r < 0)
return r;
return result;
}
int pid_is_unwaited(pid_t pid) {
/* Checks whether a PID is still valid at all, including a zombie */
if (pid < 0)
return false;
return -ESRCH;
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
return true;
@ -920,13 +1145,31 @@ bool pid_is_unwaited(pid_t pid) {
return errno != ESRCH;
}
bool pid_is_alive(pid_t pid) {
int pidref_is_unwaited(const PidRef *pid) {
int r;
if (!pidref_is_set(pid))
return -ESRCH;
if (pid->pid == 1 || pidref_is_self(pid))
return true;
r = pidref_kill(pid, 0);
if (r == -ESRCH)
return false;
if (r < 0)
return r;
return true;
}
int pid_is_alive(pid_t pid) {
int r;
/* Checks whether a PID is still valid and not a zombie */
if (pid < 0)
return false;
return -ESRCH;
if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
return true;
@ -935,10 +1178,33 @@ bool pid_is_alive(pid_t pid) {
return true;
r = get_process_state(pid);
if (IN_SET(r, -ESRCH, 'Z'))
if (r == -ESRCH)
return false;
if (r < 0)
return r;
return true;
return r != 'Z';
}
int pidref_is_alive(const PidRef *pidref) {
int r, result;
if (!pidref_is_set(pidref))
return -ESRCH;
result = pid_is_alive(pidref->pid);
if (result < 0) {
assert(result != -ESRCH);
return result;
}
r = pidref_verify(pidref);
if (r == -ESRCH)
return false;
if (r < 0)
return r;
return result;
}
int pid_from_same_root_fs(pid_t pid) {
@ -1057,7 +1323,10 @@ void valgrind_summary_hack(void) {
pid_t pid;
pid = raw_clone(SIGCHLD);
if (pid < 0)
log_emergency_errno(errno, "Failed to fork off valgrind helper: %m");
log_struct_errno(
LOG_EMERG, errno,
"MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
else if (pid == 0)
exit(EXIT_SUCCESS);
else {
@ -1104,7 +1373,7 @@ pid_t getpid_cached(void) {
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
*/
__atomic_compare_exchange_n(
(void) __atomic_compare_exchange_n(
&cached_pid,
&current_value,
CACHED_PID_BUSY,
@ -1159,6 +1428,51 @@ static void restore_sigsetp(sigset_t **ssp) {
(void) sigprocmask(SIG_SETMASK, *ssp, NULL);
}
pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) {
size_t ps;
pid_t pid;
void *mystack;
/* A wrapper around glibc's clone() call that automatically sets up a "nested" stack. Only supports
* invocations without CLONE_VM, so that we can continue to use the parent's stack mapping.
*
* Note: glibc's clone() wrapper does not synchronize malloc() locks. This means that if the parent
* is threaded these locks will be in an undefined state in the child, and hence memory allocations
* are likely going to run into deadlocks. Hence: if you use this function make sure your parent is
* strictly single-threaded or your child never calls malloc(). */
assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
/* We allocate some space on the stack to use as the stack for the child (hence "nested"). Note that
* the net effect is that the child will have the start of its stack inside the stack of the parent,
* but since they are a CoW copy of each other that's fine. We allocate one page-aligned page. But
* since we don't want to deal with differences between systems where the stack grows backwards or
* forwards we'll allocate one more and place the stack address in the middle. Except that we also
* want it page aligned, hence we'll allocate one page more. Makes 3. */
ps = page_size();
mystack = alloca(ps*3);
mystack = (uint8_t*) mystack + ps; /* move pointer one page ahead since stacks usually grow backwards */
mystack = (void*) ALIGN_TO((uintptr_t) mystack, ps); /* align to page size (moving things further ahead) */
#if HAVE_CLONE
pid = clone(fn, mystack, flags, userdata);
#else
pid = __clone2(fn, mystack, ps, flags, userdata);
#endif
if (pid < 0)
return -errno;
return pid;
}
static int fork_flags_to_signal(ForkFlags flags) {
return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM :
(flags & FORK_DEATHSIG_SIGINT) ? SIGINT :
SIGKILL;
}
int safe_fork_full(
const char *name,
const int stdio_fds[3],
@ -1170,9 +1484,12 @@ int safe_fork_full(
pid_t original_pid, pid;
sigset_t saved_ss, ss;
_unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
bool block_signals = false, block_all = false;
bool block_signals = false, block_all = false, intermediary = false;
int prio, r;
assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT));
/* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
* returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
@ -1185,9 +1502,10 @@ int safe_fork_full(
fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */
}
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
/* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
* be sure that SIGTERMs are not lost we might send to the child. */
if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) {
/* We temporarily block all signals, so that the new child has them blocked initially. This
* way, we can be sure that SIGTERMs are not lost we might send to the child. (Note that for
* FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */
assert_se(sigfillset(&ss) >= 0);
block_signals = block_all = true;
@ -1206,17 +1524,47 @@ int safe_fork_full(
saved_ssp = &saved_ss;
}
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
if (FLAGS_SET(flags, FORK_DETACH)) {
assert(!FLAGS_SET(flags, FORK_WAIT));
assert(!ret_pid);
/* Fork off intermediary child if needed */
r = is_reaper_process();
if (r < 0)
return log_full_errno(prio, r, "Failed to determine if we are a reaper process: %m");
if (!r) {
/* Not a reaper process, hence do a double fork() so we are reparented to one */
pid = fork();
if (pid < 0)
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
if (pid > 0) {
log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
return 1; /* return in the parent */
}
intermediary = true;
}
}
if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
pid = raw_clone(SIGCHLD|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
(FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
(FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0));
else
pid = fork();
if (pid < 0)
return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
if (pid > 0) {
/* We are in the parent process */
/* If we are in the intermediary process, exit now */
if (intermediary)
_exit(EXIT_SUCCESS);
/* We are in the parent process */
log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
if (flags & FORK_WAIT) {
@ -1259,8 +1607,8 @@ int safe_fork_full(
r, "Failed to rename process, ignoring: %m");
}
if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT))
if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) {
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL))
if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) {
log_full_errno(prio, errno, "Failed to set death signal: %m");
_exit(EXIT_FAILURE);
}
@ -1285,7 +1633,7 @@ int safe_fork_full(
}
}
if (flags & FORK_DEATHSIG) {
if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) {
pid_t ppid;
/* Let's see if the parent PID is still the one we started from? If not, then the parent
* already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
@ -1294,8 +1642,9 @@ int safe_fork_full(
if (ppid == 0)
/* Parent is in a different PID namespace. */;
else if (ppid != original_pid) {
log_debug("Parent died early, raising SIGTERM.");
(void) raise(SIGTERM);
int sig = fork_flags_to_signal(flags);
log_debug("Parent died early, raising %s.", signal_to_string(sig));
(void) raise(sig);
_exit(EXIT_FAILURE);
}
}
@ -1328,6 +1677,9 @@ int safe_fork_full(
log_full_errno(prio, r, "Failed to rearrange stdio fds: %m");
_exit(EXIT_FAILURE);
}
/* Turn off O_NONBLOCK on the fdio fds, in case it was left on */
stdio_disable_nonblock();
} else {
r = make_null_stdio();
if (r < 0) {
@ -1389,6 +1741,30 @@ int safe_fork_full(
return 0;
}
int pidref_safe_fork_full(
const char *name,
const int stdio_fds[3],
const int except_fds[],
size_t n_except_fds,
ForkFlags flags,
PidRef *ret_pid) {
pid_t pid;
int r, q;
assert(!FLAGS_SET(flags, FORK_WAIT));
r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
if (r < 0)
return r;
q = pidref_set_pid(ret_pid, pid);
if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
*ret_pid = PIDREF_MAKE_FROM_PID(pid);
return r;
}
int namespace_fork(
const char *outer_name,
const char *inner_name,
@ -1411,7 +1787,7 @@ int namespace_fork(
r = safe_fork_full(outer_name,
NULL,
except_fds, n_except_fds,
(flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
(flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
if (r < 0)
return r;
if (r == 0) {
@ -1633,6 +2009,212 @@ int get_process_threads(pid_t pid) {
return n;
}
int is_reaper_process(void) {
int b = 0;
/* Checks if we are running in a reaper process, i.e. if we are expected to deal with processes
* reparented to us. This simply checks if we are PID 1 or if PR_SET_CHILD_SUBREAPER was called. */
if (getpid_cached() == 1)
return true;
if (prctl(PR_GET_CHILD_SUBREAPER, (unsigned long) &b, 0UL, 0UL, 0UL) < 0)
return -errno;
return b != 0;
}
int make_reaper_process(bool b) {
if (getpid_cached() == 1) {
if (!b)
return -EINVAL;
return 0;
}
/* Some prctl()s insist that all 5 arguments are specified, others do not. Let's always specify all,
* to avoid any ambiguities */
if (prctl(PR_SET_CHILD_SUBREAPER, (unsigned long) b, 0UL, 0UL, 0UL) < 0)
return -errno;
return 0;
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(posix_spawnattr_t*, posix_spawnattr_destroy, NULL);
int posix_spawn_wrapper(
const char *path,
char * const *argv,
char * const *envp,
const char *cgroup,
PidRef *ret_pidref) {
short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
posix_spawnattr_t attr;
sigset_t mask;
int r;
/* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the
* caller will be blocked until the child either exits or exec's. The memory of the child will be
* fully shared with the memory of the parent, so that there are no copy-on-write or memory.max
* issues.
*
* Also, move the newly-created process into 'cgroup' through POSIX_SPAWN_SETCGROUP (clone3())
* if available. Note that CLONE_INTO_CGROUP is only supported on cgroup v2.
* returns 1: We're already in the right cgroup
* 0: 'cgroup' not specified or POSIX_SPAWN_SETCGROUP is not supported. The caller
* needs to call 'cg_attach' on their own */
assert(path);
assert(argv);
assert(ret_pidref);
assert_se(sigfillset(&mask) >= 0);
r = posix_spawnattr_init(&attr);
if (r != 0)
return -r; /* These functions return a positive errno on failure */
/* Initialization needs to succeed before we can set up a destructor. */
_unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
#if HAVE_PIDFD_SPAWN
_cleanup_close_ int cgroup_fd = -EBADF;
if (cgroup) {
_cleanup_free_ char *resolved_cgroup = NULL;
r = cg_get_path_and_check(
SYSTEMD_CGROUP_CONTROLLER,
cgroup,
/* suffix= */ NULL,
&resolved_cgroup);
if (r < 0)
return r;
cgroup_fd = open(resolved_cgroup, O_PATH|O_DIRECTORY|O_CLOEXEC);
if (cgroup_fd < 0)
return -errno;
r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd);
if (r != 0)
return -r;
flags |= POSIX_SPAWN_SETCGROUP;
}
#endif
r = posix_spawnattr_setflags(&attr, flags);
if (r != 0)
return -r;
r = posix_spawnattr_setsigmask(&attr, &mask);
if (r != 0)
return -r;
#if HAVE_PIDFD_SPAWN
_cleanup_close_ int pidfd = -EBADF;
r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
if (r == 0) {
r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
if (r < 0)
return r;
return FLAGS_SET(flags, POSIX_SPAWN_SETCGROUP);
}
if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
return -r;
/* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
* flags to remove the cgroup one, which is what redirects to clone3() */
flags &= ~POSIX_SPAWN_SETCGROUP;
r = posix_spawnattr_setflags(&attr, flags);
if (r != 0)
return -r;
#endif
pid_t pid;
r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
if (r != 0)
return -r;
r = pidref_set_pid(ret_pidref, pid);
if (r < 0)
return r;
return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */
}
int proc_dir_open(DIR **ret) {
DIR *d;
assert(ret);
d = opendir("/proc");
if (!d)
return -errno;
*ret = d;
return 0;
}
int proc_dir_read(DIR *d, pid_t *ret) {
assert(d);
for (;;) {
struct dirent *de;
errno = 0;
de = readdir_no_dot(d);
if (!de) {
if (errno != 0)
return -errno;
break;
}
if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
continue;
if (parse_pid(de->d_name, ret) >= 0)
return 1;
}
if (ret)
*ret = 0;
return 0;
}
int proc_dir_read_pidref(DIR *d, PidRef *ret) {
int r;
assert(d);
for (;;) {
pid_t pid;
r = proc_dir_read(d, &pid);
if (r < 0)
return r;
if (r == 0)
break;
r = pidref_set_pid(ret, pid);
if (r == -ESRCH) /* gone by now? skip it */
continue;
if (r < 0)
return r;
return 1;
}
if (ret)
*ret = PIDREF_NULL;
return 0;
}
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",
[CLD_KILLED] = "killed",

View file

@ -14,6 +14,7 @@
#include "alloc-util.h"
#include "format-util.h"
#include "macro.h"
#include "namespace-util.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@ -38,21 +39,29 @@ typedef enum ProcessCmdlineFlags {
PROCESS_CMDLINE_QUOTE_POSIX = 1 << 3,
} ProcessCmdlineFlags;
int get_process_comm(pid_t pid, char **ret);
int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
int get_process_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
int pid_get_comm(pid_t pid, char **ret);
int pidref_get_comm(const PidRef *pid, char **ret);
int pid_get_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
int pid_get_cmdline_strv(pid_t pid, ProcessCmdlineFlags flags, char ***ret);
int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char ***ret);
int get_process_exe(pid_t pid, char **ret);
int get_process_uid(pid_t pid, uid_t *ret);
int pid_get_uid(pid_t pid, uid_t *ret);
int pidref_get_uid(const PidRef *pid, uid_t *ret);
int get_process_gid(pid_t pid, gid_t *ret);
int get_process_capeff(pid_t pid, char **ret);
int get_process_cwd(pid_t pid, char **ret);
int get_process_root(pid_t pid, char **ret);
int get_process_environ(pid_t pid, char **ret);
int get_process_ppid(pid_t pid, pid_t *ret);
int pid_get_start_time(pid_t pid, uint64_t *ret);
int pidref_get_start_time(const PidRef* pid, uint64_t *ret);
int get_process_umask(pid_t pid, mode_t *ret);
int container_get_leader(const char *machine, pid_t *pid);
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
int wait_for_terminate(pid_t pid, siginfo_t *status);
typedef enum WaitFlags {
@ -74,13 +83,17 @@ void sigkill_nowaitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
int is_kernel_thread(pid_t pid);
int pid_is_kernel_thread(pid_t pid);
int pidref_is_kernel_thread(const PidRef *pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
bool pid_is_alive(pid_t pid);
bool pid_is_unwaited(pid_t pid);
int pid_is_alive(pid_t pid);
int pidref_is_alive(const PidRef *pidref);
int pid_is_unwaited(pid_t pid);
int pidref_is_unwaited(const PidRef *pidref);
int pid_is_my_child(pid_t pid);
int pidref_is_my_child(const PidRef *pidref);
int pid_from_same_root_fs(pid_t pid);
bool is_main_thread(void);
@ -141,24 +154,34 @@ void reset_cached_pid(void);
int must_be_root(void);
pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded
* programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks,
* and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if
* the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads
* or the child uses malloc(). 💣 */
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
FORK_DEATHSIG_SIGTERM = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */
FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */
FORK_REARRANGE_STDIO = 1 << 4, /* Connect 0,1,2 to specified fds or /dev/null */
FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */
FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */
FORK_WAIT = 1 << 7, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */
FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */
FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */
FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace */
FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_DEATHSIG_SIGKILL = 1 << 4, /* Set PR_DEATHSIG in the child to SIGKILL */
FORK_REARRANGE_STDIO = 1 << 5, /* Connect 0,1,2 to specified fds or /dev/null */
FORK_REOPEN_LOG = 1 << 6, /* Reopen log connection */
FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */
FORK_WAIT = 1 << 8, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 9, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_MOUNTNS_SLAVE = 1 << 10, /* Make child's mount namespace MS_SLAVE */
FORK_PRIVATE_TMP = 1 << 11, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */
FORK_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
FORK_STDOUT_TO_STDERR = 1 << 13, /* Make stdout a copy of stderr */
FORK_FLUSH_STDIO = 1 << 14, /* fflush() stdout (and stderr) before forking */
FORK_NEW_USERNS = 1 << 15, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
} ForkFlags;
int safe_fork_full(
@ -173,6 +196,18 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
}
int pidref_safe_fork_full(
const char *name,
const int stdio_fds[3],
const int except_fds[],
size_t n_except_fds,
ForkFlags flags,
PidRef *ret_pid);
static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
}
int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
int set_oom_score_adjust(int value);
@ -201,3 +236,17 @@ int setpriority_closest(int priority);
_noreturn_ void freeze(void);
int get_process_threads(pid_t pid);
int is_reaper_process(void);
int make_reaper_process(bool b);
int posix_spawn_wrapper(
const char *path,
char * const *argv,
char * const *envp,
const char *cgroup,
PidRef *ret_pidref);
int proc_dir_open(DIR **ret);
int proc_dir_read(DIR *d, pid_t *ret);
int proc_dir_read_pidref(DIR *d, PidRef *ret);

View file

@ -6,7 +6,6 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/random.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@ -28,6 +27,7 @@
#include "missing_syscall.h"
#include "missing_threads.h"
#include "parse-util.h"
#include "process-util.h"
#include "random-util.h"
#include "sha256.h"
#include "time-util.h"
@ -50,7 +50,7 @@ static void fallback_random_bytes(void *p, size_t n) {
.call_id = fallback_counter++,
.stamp_mono = now(CLOCK_MONOTONIC),
.stamp_real = now(CLOCK_REALTIME),
.pid = getpid(),
.pid = getpid_cached(),
.tid = gettid(),
};
@ -226,7 +226,7 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
if (ioctl(fd, RNDADDENTROPY, info) < 0)
return -errno;
} else {
r = loop_write(fd, seed, size, false);
r = loop_write(fd, seed, size);
if (r < 0)
return r;
}

View file

@ -12,6 +12,8 @@ typedef struct RateLimit {
usec_t begin;
} RateLimit;
#define RATELIMIT_OFF (const RateLimit) { .interval = USEC_INFINITY, .burst = UINT_MAX }
static inline void ratelimit_reset(RateLimit *rl) {
rl->num = rl->begin = 0;
}

Some files were not shown because too many files have changed in this diff Show more